[
  {
    "path": ".github/workflows/python-ci.yml",
    "content": "# This GitHub Action workflow will\n\n# - Format code using Black\n# - Build project with all dependencies\n# - Check code with flake8 linter\n# - Run tests with pytest\n\n\n# ubuntu-latest has 7GB RAM and 14GB SSD. macos-latest has 14GB RAM and 14GB SSD. Using macos assuming more RAM -> faster tests -> less timeouts\n# Nonetheless, our tests seem to need >10 GB RAM and >16,7 GB SSD Space. Test refactoring needed.\n# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources\n\nname: Build and test\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  formatting:\n    outputs:\n      new_sha: ${{ steps.sha.outputs.SHA }}\n    runs-on: macos-latest\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.ref }} # ${{ github.event.pull_request.head.sha }} \n\n      - name: Setup Python 3.8\n        uses: actions/setup-python@v2\n        with:\n          python-version: \"3.8\"\n          cache: 'pip'\n\n      - name: Setup isort and Black\n        run: |\n          python -m pip install --upgrade pip\n          pip install isort black\n\n      - name: Run isort\n        run: |\n          isort --profile black .\n\n      - name: Run Black formatter\n        run: |\n          black .\n\n      - name: Commit changes\n        uses: EndBug/add-and-commit@v8\n        with:\n          message: 'Fix styling'\n          add: '*.py'\n      \n      - name: Get commit hash\n        id: sha\n        run: |\n          sha_new=$(git rev-parse HEAD)\n          echo $sha_new\n          echo \"::set-output name=SHA::$sha_new\"\n\n\n  build:\n    if: always()\n    needs: formatting\n    runs-on: macos-latest\n\n    steps:\n    - name: Checkout to latest changes\n      uses: actions/checkout@v3\n      with:\n        ref: ${{ needs.formatting.outputs.new_sha }}\n        fetch-depth: 0\n\n    - name: Set up Python 3.8\n      uses: actions/setup-python@v2\n      with:\n        python-version: \"3.8\"\n        cache: 'pip'\n\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install flake8 pytest pytest-cov pytest-github-actions-annotate-failures\n        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi\n        pip install -e .\n\n    - name: Install model dependencies\n      run: |\n        python src/medigan/execute_model/install_model_dependencies.py\n\n  lint:\n    needs: build\n    runs-on: macos-latest\n\n    steps:\n    - name: Checkout to latest changes\n      uses: actions/checkout@v3\n      with:\n        ref: ${{ needs.formatting.outputs.new_sha }}\n        fetch-depth: 0\n\n    - name: Set up Python 3.8\n      uses: actions/setup-python@v2\n      with:\n        python-version: \"3.8\"\n        cache: 'pip'\n\n    - name: Install cached dependencies\n      run: |\n        python -m pip install --upgrade pip\n        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi\n        pip install flake8\n        pip install -e .\n   \n    - name: Lint with flake8\n      run: |\n        # stop the build if there are Python syntax errors or undefined names\n        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --max-line-length=88 --extend-ignore=E203,E501\n        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide\n        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --extend-ignore=E203,E501 --statistics\n  \n  test:\n    needs: build\n    runs-on: macos-latest #windows-latest\n\n    steps:\n    - name: Checkout to latest changes\n      uses: actions/checkout@v3\n      with:\n        ref: ${{ needs.formatting.outputs.new_sha }}\n        fetch-depth: 0\n\n    - name: Set up Python 3.8\n      uses: actions/setup-python@v2\n      with:\n        python-version: \"3.8\"\n        cache: 'pip'\n\n    - name: Install cached dependencies\n      run: |\n        python -m pip install --upgrade pip\n        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi\n        pip install pytest pytest-cov pytest-github-actions-annotate-failures\n        pip install -e .\n        python src/medigan/execute_model/install_model_dependencies.py\n      # for windows: pip install -r requirements.txt\n      # for mac/linux: if [ -f requirements.txt ]; then pip install -r requirements.txt; fi\n    - name: Test with pytest\n      run: |\n        pytest tests -W ignore::DeprecationWarning --verbose --failed-first --log-cli-level=INFO #--cov=. --cov-report html  #--capture=tee-sys\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# Further (custom)\n.idea/\n*.DS_Store\n# config/\noutput/\nmodels/*/**\n!models/00007_BEZIERCURVE_TUMOUR_MASK/**\nmodels/00007_BEZIERCURVE_TUMOUR_MASK/.DS_Store\nmodels/00007_BEZIERCURVE_TUMOUR_MASK/__pycache__/\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "# File: .readthedocs.yaml (see example: https://docs.readthedocs.io/en/stable/config-file/v2.html)\n\n# Required (see https://blog.readthedocs.com/migrate-configuration-v2/)\nversion: 2\n\n# Set the OS, Python version and other tools you might need\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.8\"\n\n# Build documentation in the \"docs/\" directory with Sphinx\nsphinx:\n  configuration: docs/source/conf.py\n\n# Optional but recommended, declare the Python requirements required\n# to build your documentation\n# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html\npython:\n  install:\n    - requirements: docs/requirements.txt\n\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "This file is Work in Progress\n# Contributing\n\n## How to Add New Models to medigan:\n\n- `medigan` motivates the reuse of trained generative models.\n\n- Models can be added via pull request by adding a model to the config in https://github.com/RichardObi/medigan-models (link stored in `medigan.constants.CONFIG_FILE_URL`).\n\n- Model contributors need to specify a link to their model package in the config. We recommend to host and link model packages on Zenodo. Reasons:\n\n    - Zenodo model packages get a static DOI. This provides clarity as to who the contributors and IP owners of each generative model in `medigan` are.\n\n    - File modification/updates under the same DOI are not possible in Zenodo. This helps to avoid security issues as package content remains static after the model is tested, verified, and added to `medigan`.\n\n    - Examples of how `medigan` model packages should be hosted on Zenodo can be found here: https://doi.org/10.5281/zenodo.5187715 and here: https://doi.org/10.5281/zenodo.5188558\n\n\n## Architectural Overview\n![medigan architecture and worklows](docs/source/_static/medigan-workflows.png)\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Richard Osuala, Grzegorz Skorupko, Noussair Lazrak\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "Pipfile",
    "content": "[[source]]\nurl = \"https://pypi.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n[packages]\nrequests = \"*\"\nopencv-contrib-python-headless = \"*\"\ntorch = \"*\"\npyyaml = \"*\"\nunion = \"*\"\npath = \"*\"\nnumpy = \"*\"\nscikit-image = \"*\"\nscipy = \"*\"\nsphinx = \"*\"\nsphinx-rtd-theme = \"*\"\npillow = \">=9.2.0\"\nvisdom = \"*\"\ndominate = \"*\"\ntorchvision = \"*\"\ntqdm = \"*\"\nsphinx-automodapi = \"*\"\n\n[requires]\npython_version = \"3.9\"\n\n[dev-packages]\n"
  },
  {
    "path": "README.md",
    "content": "<!-- # MEDIGAN -->\n<!-- ![medigan](medigan_logo_1.png) -->\n![medigan](docs/source/_static/medigan_logo.png)\n\n[![License](https://img.shields.io/github/license/RichardObi/medigan)](https://opensource.org/licenses/MIT)\n![Continuous integration](https://github.com/RichardObi/medigan/actions/workflows/python-ci.yml/badge.svg)\n[![PyPI version](https://badge.fury.io/py/medigan.svg)](https://badge.fury.io/py/medigan)\n[![Conda version](https://img.shields.io/conda/vn/conda-forge/medigan)](https://github.com/conda-forge/medigan-feedstock)\n[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6327625.svg)](https://doi.org/10.5281/zenodo.6327625)\n[![arXiv](https://img.shields.io/badge/arXiv-2209.14472-b31b1b.svg)](https://arxiv.org/abs/2209.14472)\n\n`medigan` stands for **medi**cal **g**enerative (**a**dversarial) **n**etworks. `medigan` provides user-friendly medical image synthesis and allows users to choose from a range of pretrained generative models to `generate` synthetic datasets. These synthetic datasets can be used to train or adapt AI models that perform clinical tasks such as lesion classification, segmentation or detection. \n\nSee below how medigan can be run from the command line to generate synthetic medical images.\n\n![medigan can be run directly from the command line to generate synthetic medical images](https://github.com/RichardObi/medigan/blob/main/docs/source/_static/medigan.gif \"medigan can be run directly from the command line to generate synthetic medical images.\")\n\n## Features:\n\n- :x: **Problem 1:** Data scarcity in medical imaging. \n\n- :x: **Problem 2:** Scarcity of readily reusable generative models in medical imaging.\n\n- :white_check_mark: **Solution:** `medigan`\n    1. dataset sharing via generative models :gift:\n    2. data augmentation :gift:\n    3. domain adaptation :gift:\n    4. synthetic data evaluation method testing with multi-model datasets :gift:\n\nInstead of training your own, use one of the generative models from `medigan` to generate synthetic data. \n\nSearch and find a model in `medigan` using search terms (e.g. \"Mammography\" or \"Endoscopy\"). \n\nContribute your own generative model to `medigan` to increase its visibility, re-use, and impact.\n\n\n## Available models\n\n| Output type                                                 |           Modality            |          Model type           |       Output size       |                                                        Base dataset                                                        |                                                                                                                                                                      Output examples                                                                                                                                                                       |                                                                   `model_id`                                                                   |                                    Hosted on                                    |                                      Reference                                       |\n|-------------------------------------------------------------|:-----------------------------:|:-----------------------------:|:-----------------------:|:--------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------:|\n| <sub> Breast Calcification        </sub>                    |  <sub>  mammography  </sub>   |   <sub>    dcgan     </sub>   |  <sub> 128x128 </sub>   |            <sub>  [Inbreast](https://www.academicradiology.org/article/S1076-6332(11)00451-X/fulltext)   </sub>            |                                                                                                                                                      ![sample](docs/source/_static/samples/00001.png)                                                                                                                                                      |               <sub> [`00001_DCGAN_MMG_CALC_ROI`](https://medigan.readthedocs.io/en/latest/models.html#dcgan-mmg-calc-roi) </sub>               |     <sub>[Zenodo (5187714)](https://doi.org/10.5281/zenodo.5187714) </sub>      |                                                                                      | \n| <sub> Breast Mass                 </sub>                    |  <sub>  mammography  </sub>   |   <sub>    dcgan     </sub>   |  <sub> 128x128 </sub>   |                           <sub>   [Optimam](https://doi.org/10.48550/arXiv.2004.04742)   </sub>                            |                                                                                                                                                      ![sample](docs/source/_static/samples/00002.png)                                                                                                                                                      |               <sub> [`00002_DCGAN_MMG_MASS_ROI`](https://medigan.readthedocs.io/en/latest/models.html#dcgan-mmg-mass-roi) </sub>               |     <sub>[Zenodo (5188557)](https://doi.org/10.5281/zenodo.5188557) </sub>      |     <sub>[Alyafi et al (2019)](https://doi.org/10.48550/arXiv.1909.02062) </sub>     | \n| <sub> Breast Density Transfer     </sub>                    |  <sub>  mammography  </sub>   |   <sub>   cyclegan   </sub>   |  <sub>1332x800 </sub>   |                               <sub>    [BCDR](https://bcdr.eu/information/about)     </sub>                                |                                                                                                                                                      ![sample](docs/source/_static/samples/00003.png)                                                                                                                                                      |        <sub> [`00003_CYCLEGAN_MMG_DENSITY_FULL`](https://medigan.readthedocs.io/en/latest/models.html#cyclegan-mmg-density-full) </sub>        |     <sub>[Zenodo (5547263)](https://doi.org/10.5281/zenodo.5547263) </sub>      |   <sub> [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) </sub>    | \n| <sub> Breast Mass with Mask       </sub>                    |  <sub>  mammography  </sub>   |   <sub>   pix2pix    </sub>   |  <sub> 256x256 </sub>   |                               <sub>    [BCDR](https://bcdr.eu/information/about)     </sub>                                |                                                                                                                        ![sample](docs/source/_static/samples/00004.png) <br> ![sample](docs/source/_static/samples/00004_mask.png)                                                                                                                         | <sub><sub> [`00004_PIX2PIX_MMG_MASSES_W_MASKS`](https://medigan.readthedocs.io/en/latest/models.html#pix2pix-mmg-masses-w-masks) </sub></sub>  |     <sub>[Zenodo (7093759)](https://doi.org/10.5281/zenodo.7093759) </sub>      |                                                                                      | \n| <sub> Breast Mass                 </sub>                    |  <sub>  mammography  </sub>   |   <sub>    dcgan     </sub>   |  <sub> 128x128 </sub>   |                               <sub>    [BCDR](https://bcdr.eu/information/about)     </sub>                                |                                                                                                                                                      ![sample](docs/source/_static/samples/00005.png)                                                                                                                                                      |                      <sub> [`00005_DCGAN_MMG_MASS_ROI`](https://medigan.readthedocs.io/en/latest/models.html#id1) </sub>                       |     <sub>[Zenodo (6555188)](https://doi.org/10.5281/zenodo.6555188) </sub>      |  <sub>[Szafranowska et al (2022)](https://doi.org/10.48550/arXiv.2203.04961) </sub>  | \n| <sub> Breast Mass                 </sub>                    |  <sub>  mammography  </sub>   |   <sub>   wgan-gp    </sub>   |  <sub> 128x128 </sub>   |                               <sub>    [BCDR](https://bcdr.eu/information/about)     </sub>                                |                                                                                                                                                      ![sample](docs/source/_static/samples/00006.png)                                                                                                                                                      |              <sub> [`00006_WGANGP_MMG_MASS_ROI`](https://medigan.readthedocs.io/en/latest/models.html#wgangp-mmg-mass-roi) </sub>              |     <sub>[Zenodo (6554713)](https://doi.org/10.5281/zenodo.6554713) </sub>      |  <sub>[Szafranowska et al (2022)](https://doi.org/10.48550/arXiv.2203.04961) </sub>  | \n| <sub> Brain Tumors on Flair, T1, T1c, T2 with Masks  </sub> |   <sub>  brain MRI  </sub>    | <sub>   inpaint GAN    </sub> |  <sub> 256x256 </sub>   |       <sub>    [BRATS 2018](https://wiki.cancerimagingarchive.net/pages/viewpage.action?pageId=37224922)     </sub>        | ![sample](docs/source/_static/samples/00007_F.png) <br> ![sample](docs/source/_static/samples/00007_T1.png) <br> ![sample](docs/source/_static/samples/00007_T1c.png) <br> ![sample](docs/source/_static/samples/00007_T2.png) <br> ![sample](docs/source/_static/samples/00007_mask.png) <br> ![sample](docs/source/_static/samples/00007_grade_mask.png) |                <sub> [`00007_INPAINT_BRAIN_MRI`](https://medigan.readthedocs.io/en/latest/models.html#inpaint-brain-mri) </sub>                |     <sub> [Zenodo (7041737)](https://doi.org/10.5281/zenodo.7041737) </sub>     |           <sub>[Kim et al (2020)](https://doi.org/10.1002/mp.14701) </sub>           | \n| <sub> Breast Mass (Mal/Benign)    </sub>                    |  <sub>  mammography  </sub>   |  <sub>   c-dcgan     </sub>   |  <sub> 128x128 </sub>   |              <sub>    [CBIS-DDSM](https://wiki.cancerimagingarchive.net/display/Public/CBIS-DDSM)     </sub>               |                                                                                                                                                      ![sample](docs/source/_static/samples/00008.png)                                                                                                                                                      |               <sub> [`00008_C-DCGAN_MMG_MASSES`](https://medigan.readthedocs.io/en/latest/models.html#c-dcgan-mmg-masses) </sub>               |     <sub>[Zenodo (6647349)](https://doi.org/10.5281/zenodo.6647349) </sub>      |  <sub>[Osuala et al (2024)](https://doi.org/10.48550/arXiv.2407.12669) </sub>                                                                                  |  \n| <sub> Polyp with Mask             </sub>                    |   <sub>  endoscopy  </sub>    |    <sub>   pggan   </sub>     |  <sub> 256x256 </sub>   |                                  <sub>    [HyperKvasir](https://osf.io/mh9sj/)     </sub>                                  |                                                                                                                        ![sample](docs/source/_static/samples/00009.png) <br> ![sample](docs/source/_static/samples/00009_mask.png)                                                                                                                         |      <sub> [`00009_PGGAN_POLYP_PATCHES_W_MASKS`](https://medigan.readthedocs.io/en/latest/models.html#pggan-polyp-patches-w-masks) </sub>      |     <sub>[Zenodo (6653743)](https://doi.org/10.5281/zenodo.6653743) </sub>      | <sub>[Thambawita et al (2022)](https://doi.org/10.1371/journal.pone.0267976) </sub>  | \n| <sub> Polyp with Mask             </sub>                    |   <sub>  endoscopy  </sub>    |    <sub>   fastgan </sub>     |  <sub> 256x256 </sub>   |                                  <sub>    [HyperKvasir](https://osf.io/mh9sj/)     </sub>                                  |                                                                                                                        ![sample](docs/source/_static/samples/00010.png) <br> ![sample](docs/source/_static/samples/00010_mask.png)                                                                                                                         |    <sub> [`00010_FASTGAN_POLYP_PATCHES_W_MASKS`](https://medigan.readthedocs.io/en/latest/models.html#fastgan-polyp-patches-w-masks) </sub>    |     <sub>[Zenodo (6660711)](https://doi.org/10.5281/zenodo.6660711) </sub>      | <sub>[Thambawita et al (2022)](https://doi.org/10.1371/journal.pone.0267976) </sub>  | \n| <sub> Polyp with Mask             </sub>                    |   <sub>  endoscopy  </sub>    |     <sub>   singan </sub>     |  <sub> ≈250x250 </sub>  |                                  <sub>    [HyperKvasir](https://osf.io/mh9sj/)     </sub>                                  |                                                                                                                        ![sample](docs/source/_static/samples/00011.png) <br> ![sample](docs/source/_static/samples/00011_mask.png)                                                                                                                         |     <sub> [`00011_SINGAN_POLYP_PATCHES_W_MASKS`](https://medigan.readthedocs.io/en/latest/models.html#singan-polyp-patches-w-masks) </sub>     |     <sub>[Zenodo (6667944)](https://doi.org/10.5281/zenodo.6667944) </sub>      | <sub>[Thambawita et al (2022)](https://doi.org/10.1371/journal.pone.0267976) </sub>  | \n| <sub> Breast Mass (Mal/Benign)    </sub>                    |  <sub>  mammography  </sub>   |  <sub>   c-dcgan     </sub>   |  <sub> 128x128 </sub>   |                               <sub>    [BCDR](https://bcdr.eu/information/about)     </sub>                                |                                                                                                                                                      ![sample](docs/source/_static/samples/00012.png)                                                                                                                                                      |                      <sub> [`00012_C-DCGAN_MMG_MASSES`](https://medigan.readthedocs.io/en/latest/models.html#id2) </sub>                       |     <sub>[Zenodo (6755693)](https://doi.org/10.5281/zenodo.6818095) </sub>      |                                                                                      | \n| <sub> Breast Density Transfer MLO </sub>                    |  <sub>  mammography  </sub>   |   <sub>   cyclegan   </sub>   |  <sub>1332x800 </sub>   |                          <sub>    [OPTIMAM](https://doi.org/10.48550/arXiv.2004.04742)     </sub>                          |                                                                                                                                                      ![sample](docs/source/_static/samples/00013.png)                                                                                                                                                      | <sub> [`00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO`](https://medigan.readthedocs.io/en/latest/models.html#cyclegan-mmg-density-optimam-mlo) </sub> |     <sub>[Zenodo (6818095)](https://doi.org/10.5281/zenodo.6818095) </sub>      |   <sub> [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) </sub>    | \n| <sub> Breast Density Transfer CC  </sub>                    |  <sub>  mammography  </sub>   |   <sub>   cyclegan   </sub>   |  <sub>1332x800 </sub>   |                          <sub>    [OPTIMAM](https://doi.org/10.48550/arXiv.2004.04742)     </sub>                          |                                                                                                                                                      ![sample](docs/source/_static/samples/00014.png)                                                                                                                                                      |  <sub> [`00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC`](https://medigan.readthedocs.io/en/latest/models.html#cyclegan-mmg-density-optimam-cc) </sub>  |     <sub>[Zenodo (6818103)](https://doi.org/10.5281/zenodo.6818103) </sub>      |   <sub> [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) </sub>    |  \n| <sub> Breast Density Transfer MLO </sub>                    |  <sub>  mammography  </sub>   |   <sub>   cyclegan   </sub>   |  <sub>1332x800 </sub>   |                  <sub>    [CSAW](https://link.springer.com/article/10.1007/s10278-019-00278-0)     </sub>                  |                                                                                                                                                      ![sample](docs/source/_static/samples/00015.png)                                                                                                                                                      |    <sub> [`00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO`](https://medigan.readthedocs.io/en/latest/models.html#cyclegan-mmg-density-csaw-mlo) </sub>    |     <sub>[Zenodo (6818105)](https://doi.org/10.5281/zenodo.6818105) </sub>      |   <sub> [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) </sub>    |  \n| <sub> Breast Density Transfer CC  </sub>                    |  <sub>  mammography  </sub>   |   <sub>   cyclegan   </sub>   |  <sub>1332x800 </sub>   |                  <sub>    [CSAW](https://link.springer.com/article/10.1007/s10278-019-00278-0)    </sub>                   |                                                                                                                                                      ![sample](docs/source/_static/samples/00016.png)                                                                                                                                                      |     <sub> [`00016_CYCLEGAN_MMG_DENSITY_CSAW_CC`](https://medigan.readthedocs.io/en/latest/models.html#cyclegan-mmg-density-csaw-cc) </sub>     |     <sub>[Zenodo (6818107)](https://doi.org/10.5281/zenodo.6818107) </sub>      |   <sub> [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) </sub>    | \n| <sub> Lung Nodules                </sub>                    |  <sub>  chest x-ray  </sub>   |   <sub>   dcgan      </sub>   |  <sub>128x128  </sub>   |                        <sub>    [NODE21](https://zenodo.org/record/4725881#.YxNmNuxBwXA)     </sub>                        |                                                                                                                                                      ![sample](docs/source/_static/samples/00017.png)                                                                                                                                                      |          <sub> [`00017_DCGAN_XRAY_LUNG_NODULES`](https://medigan.readthedocs.io/en/latest/models.html#dcgan-xray-lung-nodules) </sub>          |     <sub>[Zenodo (6943691)](https://doi.org/10.5281/zenodo.6943691) </sub>      |                                                                                      | \n| <sub> Lung Nodules                </sub>                    |  <sub>  chest x-ray  </sub>   |  <sub>   wgan-gp      </sub>  |  <sub>128x128  </sub>   |                        <sub>    [NODE21](https://zenodo.org/record/4725881#.YxNmNuxBwXA)     </sub>                        |                                                                                                                                                      ![sample](docs/source/_static/samples/00018.png)                                                                                                                                                      |         <sub> [`00018_WGANGP_XRAY_LUNG_NODULES`](https://medigan.readthedocs.io/en/latest/models.html#wgangp-xray-lung-nodules) </sub>         |     <sub>[Zenodo (6943761)](https://doi.org/10.5281/zenodo.6943761) </sub>      |                                                                                      | \n| <sub> Chest Xray Images           </sub>                    |  <sub>  chest x-ray  </sub>   |   <sub>   pggan      </sub>   | <sub>1024x1024  </sub>  |             <sub>    [ChestX-ray14](https://nihcc.app.box.com/v/ChestXray-NIHCC/folder/36938765345)     </sub>             |                                                                                                                                                      ![sample](docs/source/_static/samples/00019.png)                                                                                                                                                      |                 <sub> [`00019_PGGAN_CHEST_XRAY`](https://medigan.readthedocs.io/en/latest/models.html#pggan-chest-xray) </sub>                 |     <sub>[Zenodo (6943803)](https://doi.org/10.5281/zenodo.6943803) </sub>      |                                                                                      | \n| <sub> Chest Xray Images           </sub>                    |  <sub>  chest x-ray  </sub>   |   <sub>   pggan      </sub>   | <sub>1024x1024  </sub>  |             <sub>    [ChestX-ray14](https://nihcc.app.box.com/v/ChestXray-NIHCC/folder/36938765345)     </sub>             |                                                                                                                                                      ![sample](docs/source/_static/samples/00020.png)                                                                                                                                                      |                       <sub> [`00020_PGGAN_CHEST_XRAY`](https://medigan.readthedocs.io/en/latest/models.html#id3) </sub>                        |     <sub>[Zenodo (7046280)](https://doi.org/10.5281/zenodo.7046280) </sub>      |    <sub> [Segal et al (2021)](https://doi.org/10.1007/s42979-021-00720-7) </sub>     |\n| <sub> Brain T1-T2 MRI Modality Transfer </sub>              |   <sub>  brain MRI  </sub>    | <sub>   cyclegan      </sub>  |  <sub>224x192  </sub>   |                           <sub>    [CrossMoDA 2021](https://arxiv.org/abs/2201.02831)     </sub>                           |                                                                                                                                                      ![sample](docs/source/_static/samples/00021.png)                                                                                                                                                      |         <sub> [`00021_CYCLEGAN_BRAIN_MRI_T1_T2`](https://medigan.readthedocs.io/en/latest/models.html#cyclegan-brain-mri-t1-t2) </sub>         |     <sub>[Zenodo (7074555)](https://doi.org/10.5281/zenodo.7074555) </sub>      |   <sub> [Joshi et al (2022)](https://doi.org/10.1007/978-3-031-09002-8_47) </sub>    |\n| <sub> Cardiac MRI Age Transfer </sub>                       |  <sub>  cardiac MRI  </sub>   |   <sub>   wgan      </sub>    |  <sub>256x256  </sub>   |                               <sub>    [UK Biobank](https://www.ukbiobank.ac.uk/)     </sub>                               |                                                                                                                                                      ![sample](docs/source/_static/samples/00022.png)                                                                                                                                                      |               <sub> [`00022_WGAN_CARDIAC_AGING`](https://medigan.readthedocs.io/en/latest/models.html#wgan-cardiac-aging) </sub>               |     <sub>[Zenodo (7446930)](https://doi.org/10.5281/zenodo.74469305) </sub>     |    <sub> [Campello et al (2022)](https://doi.org/10.3389/fcvm.2022.983091) </sub>    |\n| <sub> Breast DCE-MRI Contrast Injection </sub>              | <sub>  breast DCE-MRI  </sub> | <sub>   pix2pixHD      </sub> | <sub>512x512  </sub>    | <sub>    [Duke Dataset](https://sites.duke.edu/mazurowski/resources/breast-cancer-mri-dataset/)     </sub>                 |                                                                                                                                                      ![sample](docs/source/_static/samples/00023.png)                                                                                                                                                      |          <sub> [`00023_PIX2PIXHD_BREAST_DCEMRI`](https://medigan.readthedocs.io/en/latest/models.html#pix2pixhd-breast-dcemri) </sub>          | <sub>[Zenodo (10210944)](https://zenodo.org/doi/10.5281/zenodo.10210944) </sub> |    <sub> [Osuala et al (2023)](https://doi.org/10.48550/arXiv.2311.10879) </sub>     |\n\nModel information can be found in:\n- [model documentation](https://medigan.readthedocs.io/en/latest/models.html) (e.g. the parameters of the models' generate functions)\n- [global.json](https://github.com/RichardObi/medigan/blob/main/config/global.json) file (e.g. metadata for model description, selection, and execution)\n- [medigan paper](https://arxiv.org/abs/2209.14472) (e.g. analysis and comparisons of models and FID scores)\n\n## Installation\nTo install the current release, simply run:\n```console\npip install medigan\n```\nOr, alternatively via conda:\n```console\nconda install -c conda-forge medigan\n```\n\n## Getting Started\nExamples and notebooks are located at [examples](examples) folder\n\nDocumentation is available at [medigan.readthedocs.io](https://medigan.readthedocs.io/en/latest/)\n\n\n### Generation example\n#### DCGAN \nCreate mammography masses with labels (malignant or benign) using a [class-conditional DCGAN model](https://arxiv.org/abs/2407.12669).\n```python\n# import medigan and initialize Generators\nfrom medigan import Generators\ngenerators = Generators()\n\n# generate 8 samples with model 8 (00008_C-DCGAN_MMG_MASSES). \n# Also, auto-install required model dependencies.\ngenerators.generate(model_id=8, num_samples=8, install_dependencies=True)\n```\n![sample](docs/source/_static/samples/c-dcgan/model8_samples.png)\n\nThe synthetic images in the top row show malignant masses (breast cancer) while the images in the bottom row show benign masses. \nGiven such images with class information, [image classification models](https://arxiv.org/abs/2407.12669) can be (pre-)trained.\n\n\n#### CYCLEGAN \nCreate mammograms translated from Low-to-High Breast Density using CYCLEGAN model\n```python\nfrom medigan import Generators\ngenerators = Generators()\n# model 3 is \"00003_CYCLEGAN_MMG_DENSITY_FULL\"\ngenerators.generate(model_id=3, num_samples=1)\n```\n![sample](docs/source/_static/samples/cyclegan/sample_image_5_low.png)\n&rarr;\n![sample](docs/source/_static/samples/cyclegan/sample_image_5_high.png)\n\n\n### Search Example\nSearch for a [model](https://medigan.readthedocs.io/en/latest/models.html) inside medigan using keywords\n```python\n# import medigan and initialize Generators\nfrom medigan import Generators\ngenerators = Generators()\n\n# list all models\nprint(generators.list_models())\n\n# search for models that have specific keywords in their config\nkeywords = ['DCGAN', 'Mammography', 'BCDR']\nresults = generators.find_matching_models_by_values(keywords)\n```\n\n### Get Model as Dataloader \nWe can directly receive a [torch.utils.data.DataLoader](https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader) object for any of medigan's generative models.\n```python\nfrom medigan import Generators\ngenerators = Generators()\n# model 4 is \"00004_PIX2PIX_MMG_MASSES_W_MASKS\"\ndataloader = generators.get_as_torch_dataloader(model_id=4, num_samples=3)\n```\n\nVisualize the contents of the dataloader.\n```python\nfrom matplotlib import pyplot as plt\nimport numpy as np\n\nplt.figure()\n# subplot with 2 rows and len(dataloader) columns\nf, img_array = plt.subplots(2, len(dataloader)) \n\nfor batch_idx, data_dict in enumerate(dataloader):\n    sample = np.squeeze(data_dict.get(\"sample\"))\n    mask = np.squeeze(data_dict.get(\"mask\"))\n    img_array[0][batch_idx].imshow(sample, interpolation='nearest', cmap='gray')\n    img_array[1][batch_idx].imshow(mask, interpolation='nearest', cmap='gray')\nplt.show()\n```\n![sample](docs/source/_static/samples/gan_sample_00004_dataloader.png)\n\n## Visualize A Model \nWith our interface, it is possible to generate sample by manually setting the conditional inputs or latent vector values. The sample is updated in realtime, so it's possible to observe how the images changes when the parameters are modified. The visualization is available only for models with accessible input latent vector. Depending on a model, a conditional input may be also available or synthetic segmentation mask.\n```python\nfrom medigan import Generators\n\ngenerators = Generators()\n# model 10 is \"00010_FASTGAN_POLYP_PATCHES_W_MASKS\"\ngenerators.visualize(10)\n```\n\n![sample](docs/source/_static/interface.png)\n\n## Contribute A Model\n\nCreate an [__init__.py](templates/examples/__init__.py) file in your model's root folder. \n\nNext, run the following code to contribute your model to medigan.\n\n- Your model will be stored on [Zenodo](https://zenodo.org/). \n\n- Also, a Github [issue](https://github.com/RichardObi/medigan/issues) will be created to add your model's metadata to medigan's [global.json](https://github.com/RichardObi/medigan/blob/main/config/global.json).\n\n- To do so, please provide a github access token ([get one here](https://github.com/settings/tokens)) and a zenodo access token ([get one here](https://zenodo.org/account/settings/applications/tokens/new/)), as shown below. After creation, the zenodo access token may take a few minutes before being recognized in zenodo API calls.\n\n```python\nfrom medigan import Generators\ngenerators = Generators()\n\n# Contribute your model\ngenerators.contribute(\n    model_id = \"00100_YOUR_MODEL\", # assign an ID\n    init_py_path =\"path/ending/with/__init__.py\",\n    model_weights_name = \"10000\",\n    model_weights_extension = \".pt\",\n    generate_method_name = \"generate\", # in __init__.py\n    dependencies = [\"numpy\", \"torch\"], \n    creator_name = \"YOUR_NAME\",\n    creator_affiliation = \"YOUR_AFFILIATION\",\n    zenodo_access_token = 'ZENODO_ACCESS_TOKEN',\n    github_access_token = 'GITHUB_ACCESS_TOKEN',\n```\nThank you for your contribution! \n\nYou will soon receive a reply in the Github [issue](https://github.com/RichardObi/medigan/issues) that you created for your model by running ```generators.contribute()```.\n\n## Contributions in General\nWe welcome contributions to medigan. Please send us an email or read the [contributing guidelines](CONTRIBUTING.md) regarding contributing to the medigan project.\n\n## Reference\n\nIf you use a medigan model in your work, please cite its respective publication ([see references](#available-models)). \n\nPlease also consider citing the medigan paper:\n\n> [Osuala, R., Skorupko, G., Lazrak, N., Garrucho, L., García, E., Joshi, S., ... & Lekadir, K. (2023). medigan: a Python library of pretrained generative models for medical image synthesis. Journal of Medical Imaging, 10(6), 061403.](https://doi.org/10.1117/1.JMI.10.6.061403)\n\nBibTeX entry:\n```bibtex\n@article{osuala2023medigan,\n  title={medigan: a Python library of pretrained generative models for medical image synthesis},\n  author={Osuala, Richard and Skorupko, Grzegorz and Lazrak, Noussair and Garrucho, Lidia and Garc{\\'\\i}a, Eloy and Joshi, Smriti and Jouide, Socayna and Rutherford, Michael and Prior, Fred and Kushibar, Kaisar and others},\n  journal={Journal of Medical Imaging},\n  volume={10},\n  number={6},\n  pages={061403},\n  year={2023},\n  publisher={SPIE}\n}\n```\n"
  },
  {
    "path": "config/candidates.json",
    "content": "{\n  \"00007_DCGAN_CT_spine_inpaint\": {\n    \"execution\": {\n      \"package_name\": \"bone-cement-injection-planning2\",\n      \"package_link\": \"https://zenodo.org/record/5898666/files/bone-cement-injection-planning2.zip?download=1\",\n      \"model_name\": \"inpaint_modelCor\",\n      \"extension\": \".pt\",\n      \"image_size\": [\n        128,\n        128\n      ],\n      \"dependencies\": [\n        \"dipy\",\n        \"dcmstack\",\n        \"simpleitk\",\n        \"napari\",\n        \"torch\",\n        \"torchvision\",\n        \"nibabel\",\n        \"opencv-python\",\n        \"scikit-image\",\n        \"scikit-learn\",\n        \"nilearn\",\n        \"pandas\",\n        \"matplotlib\"\n      ],\n      \"generate_method\": {\n        \"name\": \"generate\",\n        \"args\": {\n          \"base\": [\n            \"model_file\",\n            \"num_samples\",\n            \"output_path\",\n            \"save_images\"\n          ],\n          \"custom\": {\n            \"save_intermediate_outputs\": \"False\",\n            \"patient_dir\": \"00005_DCGAN_CT_spine_inpaint/src/data/dataset-verse19test/rawdata/\",\n            \"fracture\": 22\n          }\n        }\n      }\n    },\n    \"selection\": {\n      \"performance\": {},\n      \"use_cases\": [\n        \"segmentation\"\n      ],\n      \"organ\": [\n        \"spine\",\n        \"vertebra\"\n      ],\n      \"modality\": [\n        \"CT\",\n        \"computerised tomography\",\n        \"CAT scans\",\n        \"computed tomography scans\"\n      ],\n      \"vendors\": [],\n      \"centres\": [],\n      \"function\": [\n        \"image generation\"\n      ],\n      \"condition\": [],\n      \"dataset\": [\n        \"https://github.com/anjany/verse\"\n      ],\n      \"augmentations\": [],\n      \"generates\": [\n        \"regions of interest\",\n        \"ROI\",\n        \"CT\",\n        \"measures\",\n        \"inpainted image\"\n      ],\n      \"height\": 128,\n      \"width\": 128,\n      \"depth\": null,\n      \"type\": \"inpainting\",\n      \"license\": \"MIT\",\n      \"dataset_type\": \"public\",\n      \"privacy_preservation\": null,\n      \"tags\": [\n        \"Deformable Registration\",\n        \"VCFs\",\n        \"CT\",\n        \"GAN\",\n        \"Spine Osteoplasty\",\n        \"Inpainting\"\n      ],\n      \"year\": \"2021\"\n    },\n    \"description\": {\n      \"title\": \"Patient-specific virtual spine straightening and vertebra inpainting: An automatic framework for osteoplasty plannings\",\n      \"provided_date\": null,\n      \"trained_date\": null,\n      \"provided_after_epoch\": null,\n      \"version\": \"0.0.1\",\n      \"publication\": null,\n      \"doi\": [\n        \"10.5281/zenodo.5838222\"\n      ],\n      \"comment\": \"BICEPS takes a CT image of a patient with one or more Vertebral Compression Fractures and generates an estimation of the healthy state of the patients spine to enable treatment planning. The uploaded ZIP file contains the model weights,the __init__.py (image generation method and utils), a README.md, and the GAN model architecture (in pytorch) below the /src folder.\"\n    }\n  }\n}\n"
  },
  {
    "path": "config/global.json",
    "content": "{\n    \"00001_DCGAN_MMG_CALC_ROI\": {\n        \"execution\": {\n            \"package_name\": \"00001_DCGAN_MMG_CALC_ROI\",\n            \"package_link\": \"https://zenodo.org/record/7031735/files/00001_DCGAN_MMG_CALC_ROI.zip?download=1\",\n            \"model_name\": \"DCGAN\",\n            \"extension\": \".pt\",\n            \"image_size\": [\n                128,\n                128\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"Path\",\n                \"torch\",\n                \"opencv-contrib-python-headless\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {\n                        \"image_size\": 128\n                    }\n                },\n                \"input_latent_vector_size\": 100\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1000,\n                \"FID\": 67.60,\n                \"FID_ratio\": 0.497,\n                \"FID_RADIMAGENET\": 1.27,\n                \"FID_RADIMAGENET_ratio\": 0.197,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"image generation\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"INbreast\"\n            ],\n            \"augmentations\": [\n                \"crop and resize\",\n                \"horizontal flip\",\n                \"vertical flip\"\n            ],\n            \"generates\": [\n                \"calcification\",\n                \"calcifications\",\n                \"calcification roi\",\n                \"calcification ROI\",\n                \"calcification images\",\n                \"calcification region of interest\"\n            ],\n            \"height\": 128,\n            \"width\": 128,\n            \"depth\": null,\n            \"type\": \"DCGAN\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"128x128\",\n                \"128 x 128\",\n                \"MammoGANs\",\n                \"Microcalcification\",\n                \"Microcalcifications\"\n            ],\n            \"year\": \"2021\"\n        },\n        \"description\": {\n            \"title\": \"DCGAN Model for Mammogram Calcification Region of Interest Generation (Trained on INbreast)\",\n            \"provided_date\": \"12th May 2021\",\n            \"trained_date\": \"May 2021\",\n            \"provided_after_epoch\": 300,\n            \"version\": \"0.0.1\",\n            \"publication\": null,\n            \"doi\": [\n                \"10.5281/zenodo.5187714\"\n            ],\n            \"inputs\": [\n                \"image_size: default=128, help=128 is the image size that works with the supplied checkpoint.\"\n            ],\n            \"comment\": \"A deep convolutional generative adversarial network (DCGAN) that generates regions of interest (ROI) of mammograms containing benign and/or malignant calcifications. Pixel dimensions are 128x128. The DCGAN was trained on ROIs from the INbreast dataset (Moreira et al, 2012). The uploaded ZIP file contains the files dcgan.pt (model weights), __init__.py (image generation method and utils), a README.md, and the GAN model architecture (in pytorch) below the /src folder. Kernel size=6 used in DCGAN discriminator.\"\n        }\n    },\n    \"00002_DCGAN_MMG_MASS_ROI\": {\n        \"execution\": {\n            \"package_name\": \"MALIGN_DCGAN\",\n            \"package_link\": \"https://zenodo.org/record/6647242/files/MALIGN_DCGAN.zip?download=1\",\n            \"model_name\": \"malign_mass_gen\",\n            \"extension\": \"\",\n            \"image_size\": [\n                128,\n                128\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"torch\",\n                \"opencv-contrib-python-headless\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {}\n                },\n                \"input_latent_vector_size\": 200\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": {\n                    \"number_radiologists\": 2,\n                    \"AUC\": [\n                        0.56,\n                        0.45\n                    ],\n                    \"accuracy\": [\n                        0.48,\n                        0.61\n                    ],\n                    \"years_experience\": [\n                        7,\n                        25\n                    ]\n                },\n                \"FID_no_images\": 1000,\n                \"FID\": 80.51,\n                \"FID_ratio\": 0.358,\n                \"FID_RADIMAGENET\": 6.19,\n                \"FID_RADIMAGENET_ratio\": 0.036,\n                \"CLF_delta\": 0.06,\n                \"SEG_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {\n                        \"f1\": 0.96\n                    },\n                    \"trained_on_real\": {\n                        \"f1\": 0.90\n                    }\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [\n                \"Hologic Inc\"\n            ],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"image generation\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"Optimam\"\n            ],\n            \"augmentations\": [],\n            \"generates\": [\n                \"mass\",\n                \"masses\",\n                \"breast masses\",\n                \"mass rois\",\n                \"mass ROIs\",\n                \"mass images\",\n                \"breast mass ROIs\"\n            ],\n            \"height\": 128,\n            \"width\": 128,\n            \"depth\": null,\n            \"type\": \"DCGAN\",\n            \"dataset_type\": \"public\",\n            \"license\": \"MIT\",\n            \"privacy_preservation\": null,\n            \"year\": \"2019\",\n            \"tags\": [\n                \"Turing Test\",\n                \"Visual Turing Test\",\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"128 x 128\",\n                \"128x128\",\n                \"MammoGANs\",\n                \"Nodule\",\n                \"Nodules\",\n                \"Breast mass\"\n            ]\n        },\n        \"description\": {\n            \"title\": \"DCGAN Model for Mammogram Mass Region of Interest Generation (Trained on OPTIMAM)\",\n            \"provided_date\": null,\n            \"trained_date\": null,\n            \"provided_after_epoch\": null,\n            \"version\": null,\n            \"publication\": null,\n            \"doi\": [\n                \"10.5281/zenodo.5188557\",\n                \"10.1117/12.2543506\",\n                \"10.1117/12.2560473\"\n            ],\n            \"inputs\": [],\n            \"comment\": \"A deep convolutional generative adversarial network (DCGAN) that generates regions of interest (ROI) of mammograms containing benign and/or malignant masses. Pixel dimensions are 128x128. The DCGAN was trained on ROIs from the Optimam dataset (Halling-Brown et al, 2014). The uploaded ZIP file contains the files malign_mass_gen (model weights), and __init__.py (image generation method and pytorch GAN model architecture). Kernel size=6 used in DCGAN discriminator.\"\n        }\n    },\n    \"00003_CYCLEGAN_MMG_DENSITY_FULL\": {\n        \"execution\": {\n            \"package_name\": \"00003_CYCLEGAN_MMG_DENSITY_FULL\",\n            \"package_link\": \"https://zenodo.org/record/7093550/files/00003_CYCLEGAN_MMG_DENSITY_FULL.zip?download=1\",\n            \"model_name\": \"CycleGAN_high_density\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                1332,\n                800\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"Path\",\n                \"pyyaml\",\n                \"opencv-contrib-python-headless\",\n                \"torch\",\n                \"torchvision\",\n                \"dominate\",\n                \"visdom\",\n                \"Pillow\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"output_path\",\n                        \"save_images\",\n                        \"num_samples\"\n                    ],\n                    \"custom\": {\n                        \"translate_all_images\": false,\n                        \"input_path\": \"models/00003_CYCLEGAN_MMG_DENSITY_FULL/images\",\n                        \"image_size\": [\n                            1332,\n                            800\n                        ],\n                        \"gpu_id\": 0\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 74,\n                \"FID\": 150.16,\n                \"FID_ratio\": 0.439,\n                \"FID_RADIMAGENET\": 3.00,\n                \"FID_RADIMAGENET_ratio\": 0.265,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": 0.06,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {\n                        \"AUC\": 0.89\n                    },\n                    \"trained_on_real\": {\n                        \"AUC\": 0.83\n                    }\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"detection\",\n                \"domain-translation\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"image to image\",\n                \"image generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"BCDR\"\n            ],\n            \"augmentations\": [\n                \"resize\"\n            ],\n            \"generates\": [\n                \"full images\",\n                \"mammograms\",\n                \"full-field digital mammograms\"\n            ],\n            \"height\": 1332,\n            \"width\": 800,\n            \"depth\": null,\n            \"type\": \"CycleGAN\",\n            \"license\": \"BSD\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"CycleGANs\",\n                \"CycleGAN\",\n                \"Density\",\n                \"Breast Density\",\n                \"High Density\",\n                \"Low Density\",\n                \"ACR\"\n            ],\n            \"year\": \"2021\"\n        },\n        \"description\": {\n            \"title\": \"CycleGAN Model for Low-to-High Brest Density Mammograms Translation (Trained on BCDR)\",\n            \"provided_date\": \"12th Sep 2021\",\n            \"trained_date\": \"Sep 2021\",\n            \"provided_after_epoch\": 100,\n            \"version\": \"0.0.1\",\n            \"publication\": null,\n            \"doi\": [\n                \"https://doi.org/10.48550/arXiv.2209.09809\"\n            ],\n            \"inputs\": [\n                \"input_path: default=models/00003_CYCLEGAN_MMG_DENSITY_FULL/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n                \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n                \"gpu_id: default=0, help=the gpu to run the model on.\",\n                \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\"\n            ],\n            \"comment\": \"A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from BCDR dataset (Lopez, M. G., et al. 2012). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder.\"\n        }\n    },\n    \"00004_PIX2PIX_MMG_MASSES_W_MASKS\": {\n        \"execution\": {\n            \"package_name\": \"00004_PIX2PIX_MMG_MASSES_W_MASKS\",\n            \"package_link\": \"https://zenodo.org/record/7093760/files/00004_PIX2PIX_MMG_MASSES_W_MASKS.zip?download=1\",\n            \"model_name\": \"pix2pix_mask_to_mass_model\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                256,\n                256\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"Path\",\n                \"pyyaml\",\n                \"opencv-contrib-python-headless\",\n                \"torch\",\n                \"torchvision\",\n                \"dominate\",\n                \"visdom\",\n                \"Pillow\",\n                \"imageio\",\n                \"scikit-image\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate_GAN_images\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"output_path\",\n                        \"save_images\",\n                        \"num_samples\"\n                    ],\n                    \"custom\": {\n                        \"input_path\": \"models/00004_PIX2PIX_MMG_MASSES_W_MASKS/images\",\n                        \"image_size\": [\n                            256,\n                            256\n                        ],\n                        \"patch_size\": [\n                            32,\n                            32\n                        ],\n                        \"shapes\": [\n                            \"oval\",\n                            \"lobulated\"\n                        ],\n                        \"ssim_threshold\": 0.2,\n                        \"gpu_id\": 0\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 199,\n                \"FID\": 161.17,\n                \"FID_ratio\": 0.423,\n                \"FID_RADIMAGENET\": null,\n                \"FID_RADIMAGENET_ratio\": null,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {\n                        \"Dice\": 0.737\n                    },\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {\n                        \"Dice\": 0.865\n                    }\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"segmentation\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"mask to image\",\n                \"image generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"BCDR\"\n            ],\n            \"augmentations\": [\n                \"resize\"\n            ],\n            \"generates\": [\n                \"regions of interest\",\n                \"ROI\",\n                \"mammograms\",\n                \"patches\",\n                \"full-field digital mammograms\"\n            ],\n            \"height\": 256,\n            \"width\": 256,\n            \"depth\": null,\n            \"type\": \"pix2pix\",\n            \"license\": \"BSD\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"pix2pix\",\n                \"Pix2Pix\",\n                \"Mass segmentation\",\n                \"Breast lesion\"\n            ],\n            \"year\": \"2021\"\n        },\n        \"description\": {\n            \"title\": \"Generates synthetic patches given a random mask tiled with texture patches extracted from real images (Trained on BCDR)\",\n            \"provided_date\": \"5th Oct 2021\",\n            \"trained_date\": \"Sep 2021\",\n            \"provided_after_epoch\": 200,\n            \"version\": \"0.0.1\",\n            \"publication\": null,\n            \"doi\": [\n                \"10.5281/zenodo.5863095\"\n            ],\n            \"inputs\": [\n                \"input_path: default=models/00004_PIX2PIX_MMG_MASSES_W_MASKS/images help=inputs that are used in the pix2pix input image pool (e.g. for tiled image generation) \",\n                \"image_size: default=[256, 256] help=height and width of images.\",\n                \"patch_size: default=[32, 32] help=height and width of patches (annotation size on image).\",\n                \"shapes: default=['oval', 'lobulated'] help=the type of the mask curve shapes generated via bezier curves.\",\n                \"ssim_threshold: default=0.2, help=the SSIM threshold that images must surpass to be output.\",\n                \"gpu_id: default=0 help=the gpu to run the model.\"\n            ],\n            \"comment\": \"Generates synthetic patches given a random mask tiled with texture patches extracted from real images. The texture patches should be extracted from within the mass an outside the mass of a real image. Hence, some real ROIs are required to start with and its ideal for data augmentation purposes for mass segmentation.\"\n        }\n    },\n    \"00005_DCGAN_MMG_MASS_ROI\": {\n        \"execution\": {\n            \"package_name\": \"00005_DCGAN_MMG_MASS_ROI\",\n            \"package_link\": \"https://zenodo.org/record/7031758/files/00005_DCGAN_MMG_MASS_ROI.zip?download=1\",\n            \"model_name\": \"500\",\n            \"extension\": \".pt\",\n            \"image_size\": [\n                128,\n                128\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"torch\",\n                \"opencv-contrib-python-headless\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {}\n                },\n                \"input_latent_vector_size\": 100\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 199,\n                \"FID\": 180.04,\n                \"FID_ratio\": 0.379,\n                \"FID_RADIMAGENET\": 1.67,\n                \"FID_RADIMAGENET_ratio\": 0.593,\n                \"CLF_delta\": 0.029,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {\n                        \"f1\": 0.920,\n                        \"AUC\": 0.959,\n                        \"AUPRC\": 0.992\n                    },\n                    \"trained_on_real\": {\n                        \"f1\": 0.891,\n                        \"AUC\": 0.928,\n                        \"AUPRC\": 0.986\n                    }\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"image generation\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"BCDR\"\n            ],\n            \"augmentations\": [\n                \"horizontal flip\",\n                \"vertical flip\"\n            ],\n            \"generates\": [\n                \"mass\",\n                \"masses\",\n                \"mass roi\",\n                \"mass ROI\",\n                \"mass images\",\n                \"mass region of interest\",\n                \"nodule\",\n                \"nodule\",\n                \"nodule roi\",\n                \"nodule ROI\",\n                \"nodule images\",\n                \"nodule region of interest\"\n            ],\n            \"height\": 128,\n            \"width\": 128,\n            \"depth\": null,\n            \"type\": \"DCGAN\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Breast\",\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"128x128\",\n                \"128 x 128\",\n                \"MammoGANs\",\n                \"Masses\",\n                \"Nodules\"\n            ],\n            \"year\": \"2021\"\n        },\n        \"description\": {\n            \"title\": \"DCGAN Model for Mammogram MASS Patch Generation (Trained on BCDR)\",\n            \"provided_date\": \"Dec 2021\",\n            \"trained_date\": \"Nov 2021\",\n            \"provided_after_epoch\": 500,\n            \"version\": \"0.0.1\",\n            \"publication\": \"IWBI2022\",\n            \"doi\": [\n                \"10.48550/arXiv.2203.04961\"\n            ],\n            \"inputs\": [],\n            \"comment\": \"A deep convolutional generative adversarial network (DCGAN) that generates mass patches of mammograms. Pixel dimensions are 128x128. The DCGAN was trained on MMG patches from the BCDR dataset (Lopez et al, 2012). The uploaded ZIP file contains the files 500.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, and the GAN model architecture (in pytorch) below the /src folder.\"\n        }\n    },\n    \"00006_WGANGP_MMG_MASS_ROI\": {\n        \"execution\": {\n            \"package_name\": \"00006_WGANGP_MMG_MASS_ROI\",\n            \"package_link\": \"https://zenodo.org/record/7031763/files/00006_WGANGP_MMG_MASS_ROI.zip?download=1\",\n            \"model_name\": \"10000\",\n            \"extension\": \".pt\",\n            \"image_size\": [\n                128,\n                128\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"torch\",\n                \"opencv-contrib-python-headless\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {}\n                },\n                \"input_latent_vector_size\": 100\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 199,\n                \"FID\": 221.30,\n                \"FID_ratio\": 0.308,\n                \"FID_RADIMAGENET\": 1.80,\n                \"FID_RADIMAGENET_ratio\": 0.550,\n                \"CLF_delta\": 0.078,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {\n                        \"f1\": 0.969,\n                        \"AUC\": 0.978,\n                        \"AUPRC\": 0.996\n                    },\n                    \"trained_on_real\": {\n                        \"f1\": 0.891,\n                        \"AUC\": 0.928,\n                        \"AUPRC\": 0.986\n                    }\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"image generation\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"BCDR\"\n            ],\n            \"augmentations\": [\n                \"horizontal flip\",\n                \"vertical flip\"\n            ],\n            \"generates\": [\n                \"mass\",\n                \"masses\",\n                \"mass roi\",\n                \"mass ROI\",\n                \"mass images\",\n                \"mass region of interest\",\n                \"nodule\",\n                \"nodule\",\n                \"nodule roi\",\n                \"nodule ROI\",\n                \"nodule images\",\n                \"nodule region of interest\"\n            ],\n            \"height\": 128,\n            \"width\": 128,\n            \"depth\": null,\n            \"type\": \"WGAN-GP\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Breast\",\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"128x128\",\n                \"128 x 128\",\n                \"MammoGANs\",\n                \"Masses\",\n                \"Nodules\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"WGAN-GP Model for Mammogram MASS Patch Generation (Trained on BCDR)\",\n            \"provided_date\": \"Mar 2022\",\n            \"trained_date\": \"Mar 2022\",\n            \"provided_after_epoch\": 10000,\n            \"version\": \"1.0.0\",\n            \"publication\": \"IWBI2022\",\n            \"doi\": [\n                \"10.48550/arXiv.2203.04961\"\n            ],\n            \"inputs\": [],\n            \"comment\": \"A wasserstein generative adversarial network with gradient penalty (WGAN-GP) that generates mass patches of mammograms. Pixel dimensions are 128x128. The DCGAN was trained on MMG patches from the BCDR dataset (Lopez et al, 2012). The uploaded ZIP file contains the files 10000.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, and the GAN model architecture (in pytorch) below the /src folder.\"\n        }\n    },\n    \"00007_INPAINT_BRAIN_MRI\": {\n        \"execution\": {\n            \"package_name\": \"00007_INPAINT_BRAIN_MRI\",\n            \"package_link\": \"https://zenodo.org/records/10214796/files/00007_INPAINT_BRAIN_MRI.zip?download=1\",\n            \"model_name\": \"inp_gen\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                256,\n                256\n            ],\n            \"dependencies\": [\n                \"matplotlib\",\n                \"albumentations\",\n                \"torch\",\n                \"torchvision\",\n                \"numpy\",\n                \"pillow\",\n                \"opencv-contrib-python\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {\n                        \"image_size\": 256,\n                        \"num_inpaints_per_sample\": 2,\n                        \"randomize_input_image_order\": true,\n                        \"F_img_path\": null,\n                        \"T1_img_path\": null,\n                        \"T1c_img_path\": null,\n                        \"T2_img_path\": null,\n                        \"add_variations_to_mask\": true,\n                        \"x_center\": 130,\n                        \"y_center\": 130,\n                        \"radius_1\": 10,\n                        \"radius_2\": 15,\n                        \"radius_3\": 30\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1000,\n                \"FID\": 140.02,\n                \"FID_ratio\": 0.219,\n                \"FID_RADIMAGENET\": 5.31,\n                \"FID_RADIMAGENET_ratio\": 0.012,\n                \"CLF_delta\": null,\n                \"SEG_delta\": 0.018,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {\n                        \"dice\": 0.814\n                    },\n                    \"trained_on_real\": {\n                        \"dice\": 0.796\n                    }\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"segmentation\",\n                \"classification\",\n                \"detection\"\n            ],\n            \"organ\": [\n                \"brain\",\n                \"cranial\",\n                \"head\"\n            ],\n            \"modality\": [\n                \"MRI\",\n                \"Cranial MRI\",\n                \"Brain MRI\",\n                \"Flair\",\n                \"T1\",\n                \"T1c\",\n                \"T1 contrast-enhanced\",\n                \"T2\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"image inpainting\",\n                \"mask to image\",\n                \"circle to tumor grade\",\n                \"tumor grade to image\",\n                \"cross-modal synthesis\",\n                \"data augmentation\",\n                \"domain-adaptation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"BRATS\"\n            ],\n            \"augmentations\": [],\n            \"generates\": [\n                \"brain MRI\",\n                \"Flair\",\n                \"T1\",\n                \"T1c\",\n                \"T2\",\n                \"binary mask\",\n                \"tumor grade mask\"\n            ],\n            \"height\": 256,\n            \"width\": 256,\n            \"depth\": 1,\n            \"type\": \"Inpaint Generator\",\n            \"license\": null,\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Brain\",\n                \"Tumor\",\n                \"MRI Generation\",\n                \"Inpainting\",\n                \"Brain MRI Synthesis\",\n                \"Concentric Circle\",\n                \"Tumor Inpainting\",\n                \"Tumor Grade\",\n                \"Tumor Grading\",\n                \"Cross-Modality\",\n                \"Multi-modal synthesis\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"Tumor Inpainting Model for Generation of Flair, T1, T1c, T2 Brain MRI Images (Trained on BRATS)\",\n            \"provided_date\": \"August 2022\",\n            \"trained_date\": \"2020\",\n            \"provided_after_epoch\": null,\n            \"version\": null,\n            \"publication\": \"Medical Physics Journal\",\n            \"doi\": [\n                \"https://doi.org/10.48550/arXiv.2003.07526\",\n                \"https://doi.org/10.1002/mp.14701\"\n            ],\n            \"inputs\": [\n                \"image_size: default=256, help=the size if height and width of the generated images.\",\n                \"num_inpaints_per_sample: default=2, help=the number of tumor inpaint images per MRI modality that is generated from the same input sample\",\n                \"randomize_input_image_order: default=True, help=input image order is randomized. This helps to not exclude input images if batch generation is used.\",\n                \"F_img_path: default=None, help=The path to the folder were the input Flair MRI images are stored.\",\n                \"T1_img_path: default=None, help=The path to the folder were the input T1 MRI images are stored.\",\n                \"T1c_img_path: default=None, help=The path to the folder were the input T1c MRI images are stored.\",\n                \"T2_img_path: default=None, help=The path to the folder were the input T2 MRI images are stored.\",\n                \"add_variations_to_mask: default=True, help=This slightly varies the values of x_center, y_center, radius_1, radius_2, radius_3. If True, the same segmentation masks is still used to generate each of the 4 modality images. This is recommended as it results in higher image diversity.\",\n                \"x_center: default=130, help=the x coordinate of the concentric circle upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\",\n                \"y_center: default=130, help=the y coordinate of the concentric circle upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\",\n                \"radius_1: default=10, help=the radius of the first (inside second) of three concentric circles (necrotic and non-enhancing tumor) upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\",\n                \"radius_2: default=15, help=the radius of the second (inside third) of three concentric circles (enhancing tumor) upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\",\n                \"radius_3: default=30, help=the radius of the third of three concentric circles (edema) upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\"\n            ],\n            \"comment\": \"A Generative adversarial network (GAN) for Inpainting tumors (based on concentric circle-based tumor grade masks) into multi-modal MRI images (Flair, T1, T1c, T2) with dimensions 256x256. Model was trained on BRATS MRI Dataset (Menze et al). For more information, see publication (https://doi.org/10.1002/mp.14701). Model comes with example input image folders. Apart from that, the uploaded ZIP file contains the model checkpoint files .pth (model weight), __init__.py (image generation method and utils), a requirements.txt, the MEDIGAN metadata.json. The proposed method synthesizes brain tumor images from normal brain images and concentric circles that are simplified tumor masks. The tumor masks are defined by complex features, such as grade, appearance, size, and location. Thus, these features of the tumor masks are condensed and simplified to concentric circles. In the proposed method, the user-defined concentric circles are converted to various tumor masks through deep neural networks. The normal brain images are masked by the tumor mask, and the masked region is inpainted with the tumor images synthesized by the deep neural networks. Also see original repository at: https://github.com/KSH0660/BrainTumor\"\n        }\n    },\n    \"00008_C-DCGAN_MMG_MASSES\": {\n        \"execution\": {\n            \"package_name\": \"00008_C-DCGAN_MMG_MASSES\",\n            \"package_link\": \"https://zenodo.org/record/7382545/files/00008_C-DCGAN_MMG_MASSES.zip?download=1\",\n            \"model_name\": \"1400_ckpt_train_cbis_ddsm\",\n            \"extension\": \".pt\",\n            \"image_size\": [\n                128,\n                128\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"torch\",\n                \"opencv-contrib-python-headless\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {\n                        \"condition\": null,\n                        \"z\": null,\n                        \"is_cbisddsm_training_data\": true\n                    }\n                },\n                \"input_latent_vector_size\": 100\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 379,\n                \"FID\": 137.75,\n                \"FID_ratio\": 0.272,\n                \"FID_RADIMAGENET\": 3.05,\n                \"FID_RADIMAGENET_ratio\": 0.151,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"malignant versus benign classification\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"image generation\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"CBIS-DDSM\"\n            ],\n            \"augmentations\": [\n                \"horizontal flip\",\n                \"vertical flip\"\n            ],\n            \"generates\": [\n                \"mass\",\n                \"masses\",\n                \"mass roi\",\n                \"mass ROI\",\n                \"mass images\",\n                \"mass region of interest\",\n                \"nodule\",\n                \"nodule\",\n                \"nodule roi\",\n                \"nodule ROI\",\n                \"nodule images\",\n                \"nodule region of interest\"\n            ],\n            \"height\": 128,\n            \"width\": 128,\n            \"depth\": null,\n            \"type\": \"Conditional DCGAN\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Breast\",\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"128x128\",\n                \"128 x 128\",\n                \"MammoGANs\",\n                \"Masses\",\n                \"Nodules\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"Conditional DCGAN Model for Patch Generation of Mammogram Masses Conditioned on Biopsy Proven Malignancy Status (Trained on CBIS-DDSM)\",\n            \"provided_date\": \"November 2022\",\n            \"trained_date\": \"November 2022\",\n            \"provided_after_epoch\": [\n                1400,\n                1750\n            ],\n            \"version\": \"1.0.1\",\n            \"publication\": null,\n            \"doi\": [],\n            \"inputs\": [\n                \"condition: default=None, help=Either 0, 1 or None. Condition indicates whether a generated mass is malignant (0) or benign (1). If None, a balanced set of malignant and benign tumor images is created.\",\n                \"z: default=None, help=the input noise torch tensor for the generator. If None, this option is ignored (e.g. random input vector generation)\",\n                \"is_cbisddsm_training_data: default=True, help=Boolean indicating whether a GAN checkpoint trained on the predefined test or train dataset (predefined by cbis-ddsm dataset creators) should be used.\"\n            ],\n            \"comment\": \"A class-conditional deep convolutional generative adversarial network that generates mass patches of mammograms that are conditioned to either be benign (1) or malignant (0). Pixel dimensions are 128x128. The Cond-DCGAN was trained on MMG patches from the CBIS-DDSM (Sawyer Lee et al, 2016). The uploaded ZIP file contains the files 1750.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the used GAN training config file, a test.sh file to run the model, and two folders with a few generated images.\"\n        }\n    },\n    \"00009_PGGAN_POLYP_PATCHES_W_MASKS\": {\n        \"execution\": {\n            \"package_name\": \"ProGAN-4ch\",\n            \"package_link\": \"https://zenodo.org/record/6653744/files/ProGAN-4ch.zip?download=1\",\n            \"model_name\": \"ProGAN_300000_g\",\n            \"extension\": \".model\",\n            \"image_size\": [\n                256,\n                256,\n                4\n            ],\n            \"dependencies\": [\n                \"torch\",\n                \"torchvision\",\n                \"numpy\",\n                \"pillow\",\n                \"glob2\",\n                \"opencv-contrib-python\",\n                \"scikit-image\",\n                \"natsort\",\n                \"matplotlib\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {\n                        \"z_dim\": 128,\n                        \"save_option\": \"image_and_mask\",\n                        \"gpu_id\": null,\n                        \"channel\": 128,\n                        \"pixel_norm\": false,\n                        \"img_channels\": 4,\n                        \"tanh\": false,\n                        \"step\": 6,\n                        \"alpha\": 1\n                    }\n                },\n                \"input_latent_vector_size\": 128\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1000,\n                \"FID\": 225.85,\n                \"FID_ratio\": 0.192,\n                \"FID_RADIMAGENET\": null,\n                \"FID_RADIMAGENET_ratio\": null,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {\n                        \"dice_loss\": 0.137\n                    },\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {\n                        \"dice_loss\": 0.112\n                    }\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"segmentation\"\n            ],\n            \"organ\": [\n                \"polyps\",\n                \"colon\"\n            ],\n            \"modality\": [\n                \"endoscopy\",\n                \"gastrointestinal endoscopy\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image and mask\",\n                \"noise to image\",\n                \"noise to mask\",\n                \"image generation\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"HyperKvasir\"\n            ],\n            \"augmentations\": [\n                \"resize\",\n                \"Albumentations library augmentations\"\n            ],\n            \"generates\": [\n                \"masks\",\n                \"segmentation masks\",\n                \"polyp masks\",\n                \"endoscopy masks\",\n                \"endoscopy roi\",\n                \"endoscopy ROI\",\n                \"endoscopy images\",\n                \"endoscopy region of interest\",\n                \"polyp\",\n                \"polyps\",\n                \"polyp roi\",\n                \"polyp ROI\",\n                \"polyp images\",\n                \"polyp region of interest\"\n            ],\n            \"height\": 256,\n            \"width\": 256,\n            \"depth\": 4,\n            \"type\": \"Progressively-growing GAN (PGGAN)\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Endoscopy\",\n                \"Colonoscopy\",\n                \"Polyps\",\n                \"Polyp\",\n                \"Polyp Segmentation\",\n                \"Segmentation\",\n                \"256x256\",\n                \"256 x 256\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"PGGAN Model for Patch Generation of Polyps with Corresponding Segmentation Masks (Trained on HyperKvasir)\",\n            \"provided_date\": \"June 2022\",\n            \"trained_date\": \"June 2022\",\n            \"provided_after_epoch\": 1750,\n            \"version\": \"1.0.0\",\n            \"publication\": null,\n            \"doi\": [\n                \"https://doi.org/10.1371/journal.pone.0267976\"\n            ],\n            \"inputs\": [\n                \"gpu_id: type=int, default=None, help=0 is the first gpu, 1 is the second gpu, etc.\",\n                \"channel: type=int, default=128, help=determines how big the model is, smaller value means faster training, but less capacity of the model\",\n                \"z_dim: type=int, default=128, help=the initial latent vectors dimension, can be smaller such as 64, if the dataset is not diverse\",\n                \"pixel_norm: default=False, action=store_true, help=a normalization method inside the model, you can try use it or not depends on the dataset\",\n                \"img_channels: default=4, help=Number of channels in input data., for rgb images=3, gray=1 etc.\",\n                \"tanh: default=False, action=store_true, help=an output non-linearity on the output of Generator, you can try use it or not depends on the dataset\",\n                \"step: default=6, help=step to generate fake data. # can be 1 = 8, 2 = 16, 3 = 32, 4 = 64, 5 = 128, 6 = 256\",\n                \"alpha: default=1, help=Progressive gan parameter to set, 0 or 1\",\n                \"save_option: default=image_only, help=Options to save output, image_only, mask_only, image_and_mask, choices=[image_only,mask_only, image_and_mask]\",\n                \"num_fakes: default=1000, help=Number of fakes to generate, type=int\"\n            ],\n            \"comment\": \"A Progressively-growing generative adversarial network that generates a 4 dimensional output containing an RGB image (channels 1-3) and a segmentation mask (channel 4). The RGB images are images of polyps and the segmentation mask indicates the location and shape of the polyp on the image. Pixel dimensions are 256x256. The model was trained on gastrointestinal endoscopy imaging data from the HyperKvasir dataset by Borgli et al (2020, 'https://doi.org/10.1038/s41597-020-00622-y'). The uploaded ZIP file contains the files ProGAN_300000_g.model (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the source code from the official repository ('https://github.com/vlbthambawita/singan-seg-polyp'), and a test.sh file to run the model, and a folder 'examples/' with a few generated images.\"\n        }\n    },\n    \"00010_FASTGAN_POLYP_PATCHES_W_MASKS\": {\n        \"execution\": {\n            \"package_name\": \"00010_FASTGAN_POLYP_PATCHES_W_MASKS\",\n            \"package_link\": \"https://zenodo.org/record/7051328/files/00010_FASTGAN_POLYP_PATCHES_W_MASKS.zip?download=1\",\n            \"model_name\": \"FastGAN_all_50000\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                256,\n                256,\n                4\n            ],\n            \"dependencies\": [\n                \"torch\",\n                \"scipy\",\n                \"torchvision\",\n                \"opencv-contrib-python\",\n                \"pandas\",\n                \"easing-functions\",\n                \"scikit-image\",\n                \"matplotlib\",\n                \"ipdb\",\n                \"lmdb\",\n                \"numpy\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {\n                        \"save_option\": \"image_and_mask\",\n                        \"gpu_id\": null\n                    }\n                },\n                \"input_latent_vector_size\": 256\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1000,\n                \"FID\": 63.99,\n                \"FID_ratio\": 0.677,\n                \"FID_RADIMAGENET\": 7.32,\n                \"FID_RADIMAGENET_ratio\": 0.015,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {\n                        \"IoU\": 0.798\n                    },\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {\n                        \"IoU\": 0.827\n                    }\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"segmentation\"\n            ],\n            \"organ\": [\n                \"polyps\",\n                \"colon\"\n            ],\n            \"modality\": [\n                \"endoscopy\",\n                \"gastrointestinal endoscopy\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image and mask\",\n                \"noise to image\",\n                \"noise to mask\",\n                \"image generation\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"HyperKvasir\"\n            ],\n            \"augmentations\": [\n                \"resize\",\n                \"Albumentations library augmentations\"\n            ],\n            \"generates\": [\n                \"masks\",\n                \"segmentation masks\",\n                \"polyp masks\",\n                \"endoscopy masks\",\n                \"endoscopy roi\",\n                \"endoscopy ROI\",\n                \"endoscopy images\",\n                \"endoscopy region of interest\",\n                \"polyp\",\n                \"polyps\",\n                \"polyp roi\",\n                \"polyp ROI\",\n                \"polyp images\",\n                \"polyp region of interest\"\n            ],\n            \"height\": 256,\n            \"width\": 256,\n            \"depth\": 4,\n            \"type\": \"FastGAN\",\n            \"license\": \"GNU GENERAL PUBLIC LICENSE\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Endoscopy\",\n                \"Colonoscopy\",\n                \"Polyps\",\n                \"Polyp\",\n                \"Polyp Segmentation\",\n                \"Segmentation\",\n                \"256x256\",\n                \"256 x 256\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"FastGAN Model for Patch Generation of Polyps with Corresponding Segmentation Masks (Trained on HyperKvasir)\",\n            \"provided_date\": \"June 2022\",\n            \"trained_date\": \"June 2022\",\n            \"provided_after_epoch\": null,\n            \"version\": null,\n            \"publication\": null,\n            \"doi\": [\n                \"https://doi.org/10.1371/journal.pone.0267976\"\n            ],\n            \"inputs\": [\n                \"gpu_id: type=int, default=None, help=0 is the first gpu, 1 is the second gpu, etc.\",\n                \"save_option: default=image_only, help=Options to save output, image_only, mask_only, image_and_mask, choices=[image_only,mask_only, image_and_mask]\"\n            ],\n            \"comment\": \"A Fast generative adversarial network (FastGAN) that generates a 4 dimensional output containing an RGB image (channels 1-3) and a segmentation mask (channel 4). FASTGAN is from the paper 'Towards Faster and Stabilized GAN Training for High-fidelity Few-shot Image Synthesis' in ICLR 2021. The RGB images are images of polyps and the segmentation mask indicates the location and shape of the polyp on the image. Pixel dimensions are 256x256. The model was trained on gastrointestinal endoscopy imaging data from the HyperKvasir dataset by Borgli et al (2020, 'https://doi.org/10.1038/s41597-020-00622-y'). The uploaded ZIP file contains the files FastGAN_all_50000.pth (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the source code from the repository ('https://github.com/vlbthambawita/singan-seg-polyp'), and a test.sh file to run the model, and a folder 'examples/' with a few generated images.\"\n        }\n    },\n    \"00011_SINGAN_POLYP_PATCHES_W_MASKS\": {\n        \"execution\": {\n            \"package_name\": \"00011_SINGAN_POLYP_PATCHES_W_MASKS\",\n            \"package_link\": \"https://zenodo.org/record/7117187/files/00011_SINGAN_POLYP_PATCHES_W_MASKS.zip?download=1\",\n            \"model_name\": \"singan_seg_polyp/SinGAN-Generated/TrainedModels_1_clean/1/Gs\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                250,\n                250,\n                3\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"tqdm\",\n                \"torch\",\n                \"torchvision\",\n                \"pandas\",\n                \"PyYAML\",\n                \"scipy\",\n                \"scikit-image\",\n                \"scikit-learn\",\n                \"requests\",\n                \"natsort\",\n                \"matplotlib\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {\n                        \"model_files\": \"models/00011_SINGAN_POLYP_PATCHES_W_MASKS/singan_seg_polyp/SinGAN-Generated\",\n                        \"gen_start_scale\": 0,\n                        \"checkpoint_ids\": null,\n                        \"multiple_checkpoints\": false\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1000,\n                \"FID\": 171.15,\n                \"FID_ratio\": 0.253,\n                \"FID_RADIMAGENET\": null,\n                \"FID_RADIMAGENET_ratio\": null,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {\n                        \"f1\": 0.863\n                    },\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {\n                        \"f1\": 0.888\n                    }\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"segmentation\"\n            ],\n            \"organ\": [\n                \"polyps\",\n                \"colon\"\n            ],\n            \"modality\": [\n                \"endoscopy\",\n                \"gastrointestinal endoscopy\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image and mask\",\n                \"noise to image\",\n                \"noise to mask\",\n                \"image generation\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"HyperKvasir\"\n            ],\n            \"augmentations\": [\n                \"resize\",\n                \"Albumentations library augmentations\"\n            ],\n            \"generates\": [\n                \"masks\",\n                \"segmentation masks\",\n                \"polyp masks\",\n                \"endoscopy masks\",\n                \"endoscopy roi\",\n                \"endoscopy ROI\",\n                \"endoscopy images\",\n                \"endoscopy region of interest\",\n                \"polyp\",\n                \"polyps\",\n                \"polyp roi\",\n                \"polyp ROI\",\n                \"polyp images\",\n                \"polyp region of interest\"\n            ],\n            \"height\": 250,\n            \"width\": 250,\n            \"depth\": 3,\n            \"type\": \"SinGAN\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Endoscopy\",\n                \"Colonoscopy\",\n                \"Polyps\",\n                \"Polyp\",\n                \"Polyp Segmentation\",\n                \"Segmentation\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"SinGAN Model for Patch Generation of Polyps with Corresponding Segmentation Masks (Trained on HyperKvasir)\",\n            \"provided_date\": \"June 2022\",\n            \"trained_date\": \"June 2022\",\n            \"provided_after_epoch\": null,\n            \"version\": null,\n            \"publication\": null,\n            \"doi\": [\n                \"https://doi.org/10.1371/journal.pone.0267976\"\n            ],\n            \"inputs\": [\n                \"model_files: default=models/00011_SINGAN_POLYP_PATCHES_W_MASKS/singan_seg_polyp/SinGAN-Generated help=the folder where the checkpoints are stored | \",\n                \"gen_start_scale: default=0 help=The start for scaling (progressively increasing generator input size) in SinGAN.\",\n                \"checkpoint_ids: default=None help=A list of checkpoint ids that will be used for polyp generation. If None, all available checkpoints (i.e. 1000) or one random one (depending on 'multiple_checkpoints' arg) will be used.\",\n                \"multiple_checkpoints: default=False help=A boolean indicating if all checkpoint_ids or one random one is used for generating images, but only in case 'checkpoint_ids'==None\"\n            ],\n            \"comment\": \"A SinGAN generative adversarial network that generates a 2dimensional output image tuple containing a 3-channel RGB image and a 3-channel segmentation mask. SinGAN is from the paper 'SinGAN: Learning a Generative Model from a Single Natural Image' in ICCV 2019. The width of the outputted images varies depending on the corresponding original image. The RGB images are images of polyps and the segmentation mask indicates the location and shape of the polyp on the image. Pixel dimensions are 213x256. The model was trained on gastrointestinal endoscopy imaging data from the HyperKvasir dataset by Borgli et al (2020, 'https://doi.org/10.1038/s41597-020-00622-y'). The uploaded ZIP file contains the checkpoints in folder 'SinGAN-Generated' (model weights), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the source code from the repository ('https://github.com/vlbthambawita/singan-seg-polyp'), and a test.sh file to run the model, and a folder 'examples/' with a few generated images.\"\n        }\n    },\n    \"00012_C-DCGAN_MMG_MASSES\": {\n        \"execution\": {\n            \"package_name\": \"00012_C-DCGAN_MMG_MASSES\",\n            \"package_link\": \"https://zenodo.org/record/7031755/files/00012_C-DCGAN_MMG_MASSES.zip?download=1\",\n            \"model_name\": \"1250\",\n            \"extension\": \".pt\",\n            \"image_size\": [\n                128,\n                128\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"torch\",\n                \"opencv-contrib-python-headless\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {\n                        \"condition\": null,\n                        \"z\": null\n                    }\n                },\n                \"input_latent_vector_size\": 100\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 199,\n                \"FID\": 205.29,\n                \"FID_ratio\": 0.332,\n                \"FID_RADIMAGENET\": 5.69,\n                \"FID_RADIMAGENET_ratio\": 0.080,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"malignant versus benign classification\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"image generation\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"CBIS-DDSM\"\n            ],\n            \"augmentations\": [\n                \"horizontal flip\",\n                \"vertical flip\"\n            ],\n            \"generates\": [\n                \"mass\",\n                \"masses\",\n                \"mass roi\",\n                \"mass ROI\",\n                \"mass images\",\n                \"mass region of interest\",\n                \"nodule\",\n                \"nodule\",\n                \"nodule roi\",\n                \"nodule ROI\",\n                \"nodule images\",\n                \"nodule region of interest\"\n            ],\n            \"height\": 128,\n            \"width\": 128,\n            \"depth\": null,\n            \"type\": \"Conditional DCGAN\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Breast\",\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"128x128\",\n                \"128 x 128\",\n                \"MammoGANs\",\n                \"Masses\",\n                \"Nodules\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"Conditional DCGAN Model for Patch Generation of Mammogram Masses Conditioned on Biopsy Proven Malignancy Status (Trained on BCDR)\",\n            \"provided_date\": \"June 2022\",\n            \"trained_date\": \"June 2022\",\n            \"provided_after_epoch\": 1250,\n            \"version\": \"1.0.0\",\n            \"publication\": null,\n            \"doi\": [],\n            \"inputs\": [\n                \"condition: default=None, help=Either 0, 1 or None. Condition indicates whether a generated mass is malignant (0) or benign (1). If None, a balanced set of malignant and benign tumor images is created.\",\n                \"z: default=None, help=the input noise torch tensor for the generator. If None, this option is ignored (e.g. random input vector generation)\"\n            ],\n            \"comment\": \"A class-conditional deep convolutional generative adversarial network that generates mass patches of mammograms that are conditioned to either be benign (1) or malignant (0). Pixel dimensions are 128x128. The Cond-DCGAN was trained on MMG patches from the BCDR dataset (Lopez et al, 2012). The uploaded ZIP file contains the files 1250.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the used GAN training config file, a test.sh file to run the model, and two folders with a few generated images.\"\n        }\n    },\n    \"00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO\": {\n        \"execution\": {\n            \"package_name\": \"00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO\",\n            \"package_link\": \"https://zenodo.org/record/7093556/files/00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO.zip?download=1\",\n            \"model_name\": \"latest_net_G_A\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                1332,\n                800\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"Path\",\n                \"pyyaml\",\n                \"opencv-contrib-python-headless\",\n                \"torch\",\n                \"torchvision\",\n                \"dominate\",\n                \"visdom\",\n                \"Pillow\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"output_path\",\n                        \"save_images\",\n                        \"num_samples\"\n                    ],\n                    \"custom\": {\n                        \"translate_all_images\": false,\n                        \"input_path\": \"models/00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO/images\",\n                        \"image_size\": [\n                            1332,\n                            800\n                        ],\n                        \"gpu_id\": 0,\n                        \"low_to_high\": true\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 358,\n                \"FID\": 101.01,\n                \"FID_ratio\": 0.650,\n                \"FID_RADIMAGENET\": 1.14,\n                \"FID_RADIMAGENET_ratio\": 0.153,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"detection\",\n                \"domain-translation\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"image to image\",\n                \"image generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"OPTIMAM\"\n            ],\n            \"augmentations\": [\n                \"resize\"\n            ],\n            \"generates\": [\n                \"full images\",\n                \"mammograms\",\n                \"full-field digital mammograms\"\n            ],\n            \"height\": 1332,\n            \"width\": 800,\n            \"depth\": null,\n            \"type\": \"CycleGAN\",\n            \"license\": \"BSD\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"CycleGANs\",\n                \"CycleGAN\",\n                \"Density\",\n                \"Breast Density\",\n                \"High Density\",\n                \"Low Density\",\n                \"ACR\"\n            ],\n            \"year\": \"2021\"\n        },\n        \"description\": {\n            \"title\": \"CycleGAN Model for Low-to-High Brest Density Mammograms Translation of MLO VIEW (Trained on OPTIMAM)\",\n            \"provided_date\": \"2022\",\n            \"trained_date\": \"2022\",\n            \"provided_after_epoch\": null,\n            \"version\": \"0.0.1\",\n            \"publication\": null,\n            \"doi\": [\n                \"https://doi.org/10.48550/arXiv.2209.09809\"\n            ],\n            \"inputs\": [\n                \"input_path: default=models/00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n                \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n                \"gpu_id: default=0, help=the gpu to run the model on.\",\n                \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n                \"low_to_high: default=True, help=if true, breast density is added. If false, it is removed from the input image. A different generator of the cycleGAN is used based on this flag.\"\n            ],\n            \"comment\": \"A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from OPTIMAM dataset (Halling-Brown et al, 2014). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder.\"\n        }\n    },\n    \"00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC\": {\n        \"execution\": {\n            \"package_name\": \"00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC\",\n            \"package_link\": \"https://zenodo.org/record/7093553/files/00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC.zip?download=1\",\n            \"model_name\": \"latest_net_G_A\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                1332,\n                800\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"Path\",\n                \"pyyaml\",\n                \"opencv-contrib-python-headless\",\n                \"torch\",\n                \"torchvision\",\n                \"dominate\",\n                \"visdom\",\n                \"Pillow\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"output_path\",\n                        \"save_images\",\n                        \"num_samples\"\n                    ],\n                    \"custom\": {\n                        \"translate_all_images\": false,\n                        \"input_path\": \"models/00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC/images\",\n                        \"image_size\": [\n                            1332,\n                            800\n                        ],\n                        \"gpu_id\": 0,\n                        \"low_to_high\": true\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 350,\n                \"FID\": 73.77,\n                \"FID_ratio\": 0.564,\n                \"FID_RADIMAGENET\": 0.83,\n                \"FID_RADIMAGENET_ratio\": 0.190,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": 0.02,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {\n                        \"AUC\": 0.85\n                    },\n                    \"trained_on_real\": {\n                        \"AUC\": 0.83\n                    }\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"detection\",\n                \"domain-translation\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"image to image\",\n                \"image generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"OPTIMAM\"\n            ],\n            \"augmentations\": [\n                \"resize\"\n            ],\n            \"generates\": [\n                \"full images\",\n                \"mammograms\",\n                \"full-field digital mammograms\"\n            ],\n            \"height\": 1332,\n            \"width\": 800,\n            \"depth\": null,\n            \"type\": \"CycleGAN\",\n            \"license\": \"BSD\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"CycleGANs\",\n                \"CycleGAN\",\n                \"Density\",\n                \"Breast Density\",\n                \"High Density\",\n                \"Low Density\",\n                \"ACR\"\n            ],\n            \"year\": \"2021\"\n        },\n        \"description\": {\n            \"title\": \"CycleGAN Model for Low-to-High Brest Density Mammograms Translation of CC VIEW (Trained on OPTIMAM)\",\n            \"provided_date\": \"2022\",\n            \"trained_date\": \"2022\",\n            \"provided_after_epoch\": null,\n            \"version\": \"0.0.1\",\n            \"publication\": null,\n            \"doi\": [\n                \"https://doi.org/10.48550/arXiv.2209.09809\"\n            ],\n            \"inputs\": [\n                \"input_path: default=models/00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n                \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n                \"gpu_id: default=0, help=the gpu to run the model on.\",\n                \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n                \"low_to_high: default=True, help=if true, breast density is added. If false, it is removed from the input image. A different generator of the cycleGAN is used based on this flag.\"\n            ],\n            \"comment\": \"A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from OPTIMAM dataset (Halling-Brown et al, 2014). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder.\"\n        }\n    },\n    \"00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO\": {\n        \"execution\": {\n            \"package_name\": \"00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO\",\n            \"package_link\": \"https://zenodo.org/record/7093566/files/00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO.zip?download=1\",\n            \"model_name\": \"latest_net_G_A\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                1332,\n                800\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"Path\",\n                \"pyyaml\",\n                \"opencv-contrib-python-headless\",\n                \"torch\",\n                \"torchvision\",\n                \"dominate\",\n                \"visdom\",\n                \"Pillow\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"output_path\",\n                        \"save_images\",\n                        \"num_samples\"\n                    ],\n                    \"custom\": {\n                        \"translate_all_images\": false,\n                        \"input_path\": \"models/00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO/images\",\n                        \"image_size\": [\n                            1332,\n                            800\n                        ],\n                        \"gpu_id\": 0,\n                        \"low_to_high\": true\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 192,\n                \"FID\": 162.67,\n                \"FID_ratio\": 0.461,\n                \"FID_RADIMAGENET\": 4.07,\n                \"FID_RADIMAGENET_ratio\": 0.076,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": 0.02,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {\n                        \"AUC\": 0.85\n                    },\n                    \"trained_on_real\": {\n                        \"AUC\": 0.83\n                    }\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"detection\",\n                \"domain-translation\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"image to image\",\n                \"image generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"CSAW\"\n            ],\n            \"augmentations\": [\n                \"resize\"\n            ],\n            \"generates\": [\n                \"full images\",\n                \"mammograms\",\n                \"full-field digital mammograms\"\n            ],\n            \"height\": 1332,\n            \"width\": 800,\n            \"depth\": null,\n            \"type\": \"CycleGAN\",\n            \"license\": \"BSD\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"CycleGANs\",\n                \"CycleGAN\",\n                \"Density\",\n                \"Breast Density\",\n                \"High Density\",\n                \"Low Density\",\n                \"ACR\"\n            ],\n            \"year\": \"2021\"\n        },\n        \"description\": {\n            \"title\": \"CycleGAN Model for Low-to-High Brest Density Mammograms Translation of MLO VIEW (Trained on CSAW)\",\n            \"provided_date\": \"2022\",\n            \"trained_date\": \"2022\",\n            \"provided_after_epoch\": null,\n            \"version\": \"0.0.1\",\n            \"publication\": null,\n            \"doi\": [\n                \"https://doi.org/10.48550/arXiv.2209.09809\"\n            ],\n            \"inputs\": [\n                \"input_path: default=models/00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n                \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n                \"gpu_id: default=0, help=the gpu to run the model on.\",\n                \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n                \"low_to_high: default=True, help=if true, breast density is added. If false, it is removed from the input image. A different generator of the cycleGAN is used based on this flag.\"\n            ],\n            \"comment\": \"A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from CSAW dataset (Dembrower et al., 2020, https://doi.org/10.1007/s10278-019-00278-0). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder.\"\n        }\n    },\n    \"00016_CYCLEGAN_MMG_DENSITY_CSAW_CC\": {\n        \"execution\": {\n            \"package_name\": \"00016_CYCLEGAN_MMG_DENSITY_CSAW_CC\",\n            \"package_link\": \"https://zenodo.org/record/7093559/files/00016_CYCLEGAN_MMG_DENSITY_CSAW_CC.zip?download=1\",\n            \"model_name\": \"latest_net_G_A\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                1332,\n                800\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"Path\",\n                \"pyyaml\",\n                \"opencv-contrib-python-headless\",\n                \"torch\",\n                \"torchvision\",\n                \"dominate\",\n                \"visdom\",\n                \"Pillow\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"output_path\",\n                        \"save_images\",\n                        \"num_samples\"\n                    ],\n                    \"custom\": {\n                        \"translate_all_images\": false,\n                        \"input_path\": \"models/00016_CYCLEGAN_MMG_DENSITY_CSAW_CC/images\",\n                        \"image_size\": [\n                            1332,\n                            800\n                        ],\n                        \"gpu_id\": 0,\n                        \"low_to_high\": true\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 202,\n                \"FID\": 98.38,\n                \"FID_ratio\": 0.434,\n                \"FID_RADIMAGENET\": 2.71,\n                \"FID_RADIMAGENET_ratio\": 0.142,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"detection\",\n                \"domain-translation\"\n            ],\n            \"organ\": [\n                \"breast\",\n                \"breasts\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MMG\",\n                \"Mammography\",\n                \"Mammogram\",\n                \"full-field digital\",\n                \"full-field digital MMG\",\n                \"full-field MMG\",\n                \"full-field Mammography\",\n                \"digital Mammography\",\n                \"digital MMG\",\n                \"x-ray mammography\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"image to image\",\n                \"image generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"CSAW\"\n            ],\n            \"augmentations\": [\n                \"resize\"\n            ],\n            \"generates\": [\n                \"full images\",\n                \"mammograms\",\n                \"full-field digital mammograms\"\n            ],\n            \"height\": 1332,\n            \"width\": 800,\n            \"depth\": null,\n            \"type\": \"CycleGAN\",\n            \"license\": \"BSD\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Mammogram\",\n                \"Mammography\",\n                \"Digital Mammography\",\n                \"Full field Mammography\",\n                \"Full-field Mammography\",\n                \"CycleGANs\",\n                \"CycleGAN\",\n                \"Density\",\n                \"Breast Density\",\n                \"High Density\",\n                \"Low Density\",\n                \"ACR\"\n            ],\n            \"year\": \"2021\"\n        },\n        \"description\": {\n            \"title\": \"CycleGAN Model for Low-to-High Brest Density Mammograms Translation of CC VIEW (Trained on CSAW)\",\n            \"provided_date\": \"2022\",\n            \"trained_date\": \"2022\",\n            \"provided_after_epoch\": null,\n            \"version\": \"0.0.1\",\n            \"publication\": null,\n            \"doi\": [\n                \"https://doi.org/10.48550/arXiv.2209.09809\"\n            ],\n            \"inputs\": [\n                \"input_path: default=models/00016_CYCLEGAN_MMG_DENSITY_CSAW_CC/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n                \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n                \"gpu_id: default=0, help=the gpu to run the model on.\",\n                \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n                \"low_to_high: default=True, help=if true, breast density is added. If false, it is removed from the input image. A different generator of the cycleGAN is used based on this flag.\"\n            ],\n            \"comment\": \"A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from CSAW dataset (Dembrower et al., 2020, https://doi.org/10.1007/s10278-019-00278-0). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder.\"\n        }\n    },\n    \"00017_DCGAN_XRAY_LUNG_NODULES\": {\n        \"execution\": {\n            \"package_name\": \"00017_DCGAN_NODE21\",\n            \"package_link\": \"https://zenodo.org/record/6943692/files/00017_DCGAN_NODE21.zip?download=1\",\n            \"model_name\": \"model\",\n            \"extension\": \".pt\",\n            \"image_size\": [\n                128,\n                128,\n                1\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"tqdm\",\n                \"torch\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {}\n                },\n                \"input_latent_vector_size\": 120\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1476,\n                \"FID\": 126.78,\n                \"FID_ratio\": 0.192,\n                \"FID_RADIMAGENET\": null,\n                \"FID_RADIMAGENET_ratio\": null,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\"\n            ],\n            \"organ\": [\n                \"lung\",\n                \"chest\",\n                \"thorax\"\n            ],\n            \"modality\": [\n                \"x-ray\",\n                \"xray\",\n                \"CXR\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"Node21\"\n            ],\n            \"augmentations\": [\n                \"horizontal flip\",\n                \"vertical flip\"\n            ],\n            \"generates\": [\n                \"lung nodules\",\n                \"nodules\",\n                \"lung roi\",\n                \"lung region of interest\",\n                \"patches\"\n            ],\n            \"height\": 128,\n            \"width\": 128,\n            \"depth\": 1,\n            \"type\": \"DCGAN\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Thoracic xray\",\n                \"xray\",\n                \"x-ray\",\n                \"Thorax\",\n                \"Lung\",\n                \"Nodules\",\n                \"Lung Cancer\",\n                \"Lung Tumor\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"DCGAN Model for Patch Generation of Lung Nodules (Trained on Node21)\",\n            \"provided_date\": \"June 2022\",\n            \"trained_date\": \"June 2022\",\n            \"provided_after_epoch\": null,\n            \"version\": null,\n            \"publication\": null,\n            \"doi\": [],\n            \"inputs\": [],\n            \"comment\": \"An unconditional deep convolutional generative adversarial network (DCGAN) that generates lung nodule regions-of-interest patches based on chest xray (CXR) images. The pixel dimension of the generated patches is 128x128. The WGANGP was trained on cropped patches from CXR images from the NODE21 dataset (Sogancioglu et al, 2021). The uploaded ZIP file contains the files model.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata.json file, the used GAN training config file, a test.sh file to run the model, and an /image folder with a few generated example images.\"\n        }\n    },\n    \"00018_WGANGP_XRAY_LUNG_NODULES\": {\n        \"execution\": {\n            \"package_name\": \"00018_WGANGP_NODE21\",\n            \"package_link\": \"https://zenodo.org/record/6943762/files/00018_WGANGP_NODE21.zip?download=1\",\n            \"model_name\": \"model\",\n            \"extension\": \".pt\",\n            \"image_size\": [\n                128,\n                128,\n                1\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"tqdm\",\n                \"torch\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {}\n                },\n                \"input_latent_vector_size\": 100\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1476,\n                \"FID\": 211.47,\n                \"FID_ratio\": 0.115,\n                \"FID_RADIMAGENET\": null,\n                \"FID_RADIMAGENET_ratio\": null,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\"\n            ],\n            \"organ\": [\n                \"lung\",\n                \"chest\",\n                \"thorax\"\n            ],\n            \"modality\": [\n                \"x-ray\",\n                \"xray\",\n                \"CXR\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"Node21\"\n            ],\n            \"augmentations\": [\n                \"horizontal flip\",\n                \"vertical flip\"\n            ],\n            \"generates\": [\n                \"lung nodules\",\n                \"nodules\",\n                \"lung roi\",\n                \"lung region of interest\",\n                \"patches\"\n            ],\n            \"height\": 128,\n            \"width\": 128,\n            \"depth\": 1,\n            \"type\": \"WGANGP\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Thoracic xray\",\n                \"xray\",\n                \"x-ray\",\n                \"Thorax\",\n                \"Lung\",\n                \"Nodules\",\n                \"Lung Cancer\",\n                \"Lung Tumor\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"WGANGP Model for Patch Generation of Lung Nodules (Trained on Node21)\",\n            \"provided_date\": \"June 2022\",\n            \"trained_date\": \"June 2022\",\n            \"provided_after_epoch\": null,\n            \"version\": null,\n            \"publication\": null,\n            \"doi\": [],\n            \"inputs\": [],\n            \"comment\": \"An unconditional wasserstein generative adversarial network with gradient penalty (WGAN_GP) that generates lung nodule regions-of-interest patches based on chest xray (CXR) images. The pixel dimension of the generated patches is 128x128. The WGANGP was trained on cropped patches from CXR images from the NODE21 dataset (Sogancioglu et al, 2021). The uploaded ZIP file contains the files model.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata.json file, the used GAN training config file, a test.sh file to run the model, and an /image folder with a few generated example images.\"\n        }\n    },\n    \"00019_PGGAN_CHEST_XRAY\": {\n        \"execution\": {\n            \"package_name\": \"00019_PGGAN_NODE21\",\n            \"package_link\": \"https://zenodo.org/record/7047097/files/00019_PGGAN_CHEST_XRAY.zip?download=1\",\n            \"model_name\": \"model\",\n            \"extension\": \".pt\",\n            \"image_size\": [\n                1024,\n                1024,\n                1\n            ],\n            \"dependencies\": [\n                \"numpy\",\n                \"tqdm\",\n                \"torch\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {}\n                },\n                \"input_latent_vector_size\": 1024\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1000,\n                \"FID\": 96.74,\n                \"FID_ratio\": 0.297,\n                \"FID_RADIMAGENET\": 0.77,\n                \"FID_RADIMAGENET_ratio\": 0.243,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"detection\"\n            ],\n            \"organ\": [\n                \"lung\",\n                \"chest\",\n                \"thorax\"\n            ],\n            \"modality\": [\n                \"x-ray\",\n                \"xray\",\n                \"CXR\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"Node21\"\n            ],\n            \"augmentations\": [\n                \"horizontal flip\",\n                \"vertical flip\"\n            ],\n            \"generates\": [\n                \"chest xray\",\n                \"CXR\",\n                \"thoracic xray\",\n                \"lung xray\",\n                \"lung xray\"\n            ],\n            \"height\": 1024,\n            \"width\": 1024,\n            \"depth\": 1,\n            \"type\": \"PGGAN\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Thoracic xray\",\n                \"xray\",\n                \"x-ray\",\n                \"Thorax\",\n                \"Lung\",\n                \"Nodules\",\n                \"Lung Cancer\",\n                \"Lung Tumor\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"PGGAN Model for Generation of Chest XRAY (CXR) Images (Trained on the ChestX-ray14 Dataset)\",\n            \"provided_date\": \"June 2022\",\n            \"trained_date\": \"June 2022\",\n            \"provided_after_epoch\": null,\n            \"version\": null,\n            \"publication\": null,\n            \"doi\": [],\n            \"inputs\": [],\n            \"comment\": \"An unconditional Progressively-growing generative adversarial network (PGGAN) that generates chest xray (CXR) images with pixel dimensions 1024x1024. The PGGAN was trained on CXR images from the ChestX-ray14 dataset (Wang et al., 2017, Paper: https://arxiv.org/pdf/1705.02315.pdf, Data: https://nihcc.app.box.com/v/ChestXray-NIHCC). The uploaded ZIP file contains the files model.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata.json file, the used GAN training config file, a test.sh file to run the model, and an /image folder with a few generated example images.\"\n        }\n    },\n    \"00020_PGGAN_CHEST_XRAY\": {\n        \"execution\": {\n            \"package_name\": \"00020_PGGAN_CHEST_XRAY\",\n            \"package_link\": \"https://zenodo.org/record/7047295/files/00020_PGGAN_CHEST_XRAY.zip?download=1\",\n            \"model_name\": \"Final_Full_Model\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n                1024,\n                1024,\n                3\n            ],\n            \"dependencies\": [\n                \"pytorch-lightning==1.2.10\",\n                \"torch\",\n                \"torchvision\",\n                \"matplotlib\",\n                \"pillow\",\n                \"numpy\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {\n                        \"image_size\": 1024,\n                        \"resize_pixel_dim\": null\n                    }\n                },\n                \"input_latent_vector_size\": 512\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1000,\n                \"FID\": 52.17,\n                \"FID_ratio\": 0.543,\n                \"FID_RADIMAGENET\": 2.83,\n                \"FID_RADIMAGENET_ratio\": 0.071,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {\n                        \"AUC\": 0.878\n                    },\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {\n                        \"AUC\": 0.947\n                    }\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"classification\",\n                \"detection\"\n            ],\n            \"organ\": [\n                \"lung\",\n                \"chest\",\n                \"thorax\"\n            ],\n            \"modality\": [\n                \"x-ray\",\n                \"xray\",\n                \"CXR\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"noise to image\",\n                \"unconditional generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [],\n            \"dataset\": [\n                \"ChestX-ray14\"\n            ],\n            \"augmentations\": [],\n            \"generates\": [\n                \"chest xray\",\n                \"CXR\",\n                \"thoracic xray\",\n                \"lung xray\",\n                \"lung xray\"\n            ],\n            \"height\": 1028,\n            \"width\": 1028,\n            \"depth\": 3,\n            \"type\": \"PGGAN\",\n            \"license\": \"MIT\",\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Thoracic xray\",\n                \"xray\",\n                \"x-ray\",\n                \"Thorax\",\n                \"Lung\",\n                \"Nodules\",\n                \"Lung Cancer\",\n                \"Lung Tumor\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"PGGAN Model for Generation of Chest XRAY (CXR) Images (Trained on ChestX-ray14 Dataset)\",\n            \"provided_date\": \"September 2022\",\n            \"trained_date\": \"2021\",\n            \"provided_after_epoch\": null,\n            \"version\": null,\n            \"publication\": null,\n            \"doi\": [\n                \"https://doi.org/10.1007/s42979-021-00720-7\"\n            ],\n            \"inputs\": [\n                \"image_size: default=1024, help=the size if height and width of the generated images\",\n                \"resize_pixel_dim: default=None, help=Resizing of generated images via the pillow PIL image library.\"\n            ],\n            \"comment\": \"An unconditional Progressively-growing generative adversarial network (PGGAN) that generates chest xray (CXR) images with pixel dimensions 1024x1024. The PGGAN was trained on CXR images based on ChestX-ray14 dataset (Wang et al. 2017, Paper: https://arxiv.org/pdf/1705.02315.pdf, Data: https://nihcc.app.box.com/v/ChestXray-NIHCC). The uploaded ZIP file contains the model weights checkpoint file, __init__.py (image generation method and utils), a requirements.txt, the MEDIGAN metadata.json file, a test.sh file to run the model, and an /image folder with a few generated example images.\"\n        }\n    },\n    \"00021_CYCLEGAN_BRAIN_MRI_T1_T2\": {\n        \"execution\": {\n            \"package_name\": \"00021_CYCLEGAN_BRAIN_MRI_T1_T2\",\n            \"package_link\": \"https://zenodo.org/record/7113464/files/00021_CYCLEGAN_BRAIN_MRI_T1_T2.zip?download=1\",\n            \"model_name\": \"netG_T1toT2_checkpoint\",\n            \"extension\": \".pth.tar\",\n            \"image_size\": [\n                \"224\",\n                \"192\"\n            ],\n            \"dependencies\": [\n                \"matplotlib\",\n                \"Pillow\",\n                \"torch\",\n                \"torchvision\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"num_samples\",\n                        \"output_path\",\n                        \"save_images\"\n                    ],\n                    \"custom\": {\n                        \"input_path\": \"models/00021_CYCLEGAN_BRAIN_MRI_T1_T2/inputs/T1\",\n                        \"gpu_id\": \"0\",\n                        \"translate_all_images\": false,\n                        \"T1_to_T2\": true\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {\n                \"SSIM\": null,\n                \"MSE\": null,\n                \"NSME\": null,\n                \"PSNR\": null,\n                \"IS\": null,\n                \"turing_test\": null,\n                \"FID_no_images\": 1000,\n                \"FID\": 59.49,\n                \"FID_ratio\": 0.410,\n                \"FID_RADIMAGENET\": 1.45,\n                \"FID_RADIMAGENET_ratio\": 0.014,\n                \"CLF_delta\": null,\n                \"SEG_delta\": null,\n                \"DET_delta\": null,\n                \"CLF\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"SEG\": {\n                    \"trained_on_fake\": {\n                        \"dice\": 0.712,\n                        \"dice_tumor\": 0.712,\n                        \"dice_cochlea\": 0.478\n                    },\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                },\n                \"DET\": {\n                    \"trained_on_fake\": {},\n                    \"trained_on_real_and_fake\": {},\n                    \"trained_on_real\": {}\n                }\n            },\n            \"use_cases\": [\n                \"segmentation\"\n            ],\n            \"organ\": [\n                \"Brain\"\n            ],\n            \"modality\": [\n                \"T1\",\n                \"T2\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [],\n            \"condition\": [],\n            \"dataset\": [\n                \"CrossMoDA 2021\"\n            ],\n            \"augmentations\": [\n                \"HorizontalFlip\",\n                \"Rotation\"\n            ],\n            \"generates\": [\n                \"Brain\",\n                \"2D\",\n                \"MRI\",\n                \"T1\",\n                \"T2\"\n            ],\n            \"height\": 224.0,\n            \"width\": 192.0,\n            \"depth\": null,\n            \"type\": \"CycleGAN\",\n            \"license\": null,\n            \"dataset_type\": \"public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Domain Adaptation\",\n                \"Brain MRI\",\n                \"Vestibular Schwanomma\",\n                \"Segmentation\",\n                \"Cross Modal Domain Translation\"\n            ],\n            \"year\": 2021\n        },\n        \"description\": {\n            \"title\": \"CycleGAN Brain MRI T1-T2 translation (trained on CrossMoDA 2021 dataset)\",\n            \"provided_date\": \"2022\",\n            \"trained_date\": \"2021\",\n            \"provided_after_epoch\": 65,\n            \"version\": \"1\",\n            \"publication\": \"BrainLes 2022 MICCAI workshop paper\",\n            \"doi\": [\n                \"10.1007/978-3-031-09002-8_47\"\n            ],\n            \"inputs\": [\n                \"input_path: default=models/00021_CYCLEGAN_BRAIN_MRI_T1_T2/inputs/T1, help=the path to .png brain MRI images that are translated from T1 to T2 or vice versa. \",\n                \"image_size: default=[224, 192], help=list with image height and width. \",\n                \"gpu_id: default=0, help=the gpu to run the model on.\",\n                \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n                \"T1_to_T2: default=True, help=if true, generator for T1 to T2 translation is used. If false, the translation is done from T2 to T1 instead. Need to adjust input path in this case e.g. models/00021_CYCLEGAN_BRAIN_MRI_T1_T2/inputs/T2 instead of models/00021_CYCLEGAN_BRAIN_MRI_T1_T2/inputs/T1. A different generator of the cycleGAN is used based on this flag.\"\n            ],\n            \"comment\": \"In recent years, deep learning models have considerably advanced the performance of segmentation tasks on Brain Magnetic Resonance Imaging (MRI). However, these models show a considerable performance drop when they are evaluated on unseen data from a different distribution. Since annotation is often a hard and costly task requiring expert supervision, it is necessary to develop ways in which existing models can be adapted to the unseen domains without any additional labelled information. In this work, we explore one such technique which extends the CycleGAN [2] architecture to generate label-preserving data in the target domain. The synthetic target domain data is used to train the nn-UNet [3] framework for the task of multi-label segmentation. The experiments are conducted and evaluated on the dataset [1] provided in the ‘Cross-Modality Domain Adaptation for Medical Image Segmentation’ challenge [23] for segmentation of vestibular schwannoma (VS) tumour and cochlea on contrast enhanced (ceT1) and high resolution (hrT2) MRI scans. In the proposed approach, our model obtains dice scores (DSC) 0.73 and 0.49 for tumour and cochlea respectively on the validation set of the dataset. This indicates the applicability of the proposed technique to real-world problems where data may be obtained by different acquisition protocols as in [1] where hrT2 images are more reliable, safer, and lower-cost alternative to ceT1.\"\n        }\n    },\n    \"00022_WGAN_CARDIAC_AGING\": {\n        \"execution\": {\n            \"package_name\": \"00022_WGAN_CARDIAC_AGING\",\n            \"package_link\": \"https://zenodo.org/record/7494368/files/00022_WGAN_CARDIAC_AGING.zip?download=1\",\n            \"model_name\": \"model\",\n            \"extension\": \".ckpt\",\n            \"image_size\": [\n                256,\n                256\n            ],\n            \"dependencies\": [\n                \"nibabel==3.2.1\",\n                \"pytorch-lightning==1.4.7\",\n                \"pandas\",\n                \"comet-ml\",\n                \"monai<=1.0.1\",\n                \"grad-cam\",\n                \"matplotlib\",\n                \"monai[skimage]\",\n                \"munch==2.5.0\",\n                \"pillow>=7.0.0\",\n                \"ffmpeg-python==0.2.0\",\n                \"torchmetrics==0.6.0\"\n            ],\n            \"generate_method\": {\n                \"name\": \"generate\",\n                \"args\": {\n                    \"base\": [\n                        \"model_file\",\n                        \"output_path\",\n                        \"save_images\",\n                        \"num_samples\"\n                    ],\n                    \"custom\": {\n                        \"image_paths_input\": [\n                            \"models/00022_WGAN_CARDIAC_AGING/sample_image.png\",\n                            \"models/00022_WGAN_CARDIAC_AGING/sample_image.png\",\n                            \"models/00022_WGAN_CARDIAC_AGING/sample_image.png\"\n                        ],\n                        \"aging_input\": [\n                            -4,\n                            10,\n                            2\n                        ],\n                        \"data_type\": \"2d\",\n                        \"view\": \"la\",\n                        \"subcat\": \"2ch\"\n                    }\n                }\n            }\n        },\n        \"selection\": {\n            \"performance\": {},\n            \"use_cases\": [\n                \"classification\",\n                \"segmentation\"\n            ],\n            \"organ\": [\n                \"heart\",\n                \"chest\"\n            ],\n            \"modality\": [\n                \"MRI\",\n                \"Cardiac imaging\",\n                \"Cardiography\",\n                \"full-field digital\"\n            ],\n            \"vendors\": [],\n            \"centres\": [],\n            \"function\": [\n                \"image to image\",\n                \"image generation\",\n                \"data augmentation\"\n            ],\n            \"condition\": [\n                \"age\"\n            ],\n            \"dataset\": [\n                \"UK Biobank\"\n            ],\n            \"augmentations\": [\n                \"resize\"\n            ],\n            \"generates\": [\n                \"cardiac image\",\n                \"full-field digital\"\n            ],\n            \"height\": 256,\n            \"width\": 256,\n            \"depth\": null,\n            \"type\": \"pix2pix\",\n            \"license\": null,\n            \"dataset_type\": \"non-public\",\n            \"privacy_preservation\": null,\n            \"tags\": [\n                \"Cardiac imaging\",\n                \"pix2pix\",\n                \"Pix2Pix\"\n            ],\n            \"year\": \"2022\"\n        },\n        \"description\": {\n            \"title\": \"Generates cardiac images with age offset from real images (Trained on UK Biobank)\",\n            \"provided_date\": \"2022\",\n            \"trained_date\": \"2022\",\n            \"provided_after_epoch\": 299,\n            \"version\": \"0.0.1\",\n            \"publication\": \"https://www.frontiersin.org/articles/10.3389/fcvm.2022.983091\",\n            \"doi\": [\n                \"\"\n            ],\n            \"inputs\": [\n                \"input_image_paths: default=[\\\"models/00022_WGAN_CARDIAC_AGING/sample_image.png\\\"] help=List of image paths to apply aging.\",\n                \"aging_input: default=[-4] help=List of age offset values for each image.\",\n                \"data_type: default=\\\"2d\\\" help=\",\n                \"view: default=\\\"la\\\" help=\",\n                \"subcat: default=\\\"2ch\\\", help=\"\n            ],\n            \"comment\": \"Conditional WGAN-GP Model for Cardiac Image generation with age offset (Trained on UK Biobank). A conditional wasserstein generative adversarial network with gradient penalty (WGAN_GP) that generates MRI cardiac images. The pixel dimension of the generated images is 256x256. The uploaded ZIP file contains the files model.ckpt (model weights), __init__.py (image generation method and utils), a requirements.txt, and the used GAN training config file. A sample_image.png is provided for example generation.\"\n        }\n    },\n    \"00023_PIX2PIXHD_BREAST_DCEMRI\": {\n         \"execution\": {\n            \"package_name\": \"00023\",\n            \"package_link\": \"https://zenodo.org/records/10215478/files/00023.zip?download=1\",\n            \"model_name\": \"30_net_G\",\n            \"extension\": \".pth\",\n            \"image_size\": [\n               512, 512\n            ],\n            \"dependencies\": [\n               \"numpy\",\n               \"torch\",\n               \"torchvision\",\n               \"pillow\"\n            ],\n            \"generate_method\": {\n               \"name\": \"generate\",\n               \"args\": {\n                  \"base\": [\n                     \"model_file\",\n                     \"num_samples\",\n                     \"output_path\",\n                     \"save_images\"\n                  ],\n                  \"custom\": {\n                     \"input_path\": \"input/\",\n                     \"image_size\": \"512\",\n                     \"gpu_id\": \"0\"\n                  }\n               }\n            }\n         },\n         \"selection\": {\n            \"performance\": {\n               \"SSIM\": 0.726,\n               \"MSE\": 34.88,\n               \"NSME\": null,\n               \"PSNR\": 32.91,\n               \"IS\": null,\n               \"FID\": 28.71,\n               \"turing_test\": \"\",\n               \"downstream_task\": {\n                  \"CLF\": {\n                     \"trained_on_fake\": {\n                        \"accuracy\": null,\n                        \"precision\": null,\n                        \"recall\": null,\n                        \"f1\": null,\n                        \"specificity\": null,\n                        \"AUROC\": null,\n                        \"AUPRC\": null\n                     },\n                     \"trained_on_real_and_fake\": {},\n                     \"trained_on_real\": {}\n                  },\n                  \"SEG\": {\n                     \"trained_on_fake\": {\n                        \"dice\": 0.687,\n                        \"jaccard\": null,\n                        \"accuracy\": null,\n                        \"precision\": null,\n                        \"recall\": null,\n                        \"f1\": null\n                     },\n                     \"trained_on_real_and_fake\": {\n                        \"dice\": \"0.797\"\n                     },\n                     \"trained_on_real\": {\n                        \"dice\": \"0.790\"\n                     }\n                  }\n               }\n            },\n            \"use_cases\": [\n               \"segmentation\",\n               \"tumour localization\",\n               \"classification\",\n               \"simulation\"\n            ],\n            \"organ\": [\n               \"breast\"\n            ],\n            \"modality\": [\n               \"dce-mri\",\n               \"mri\",\n               \"t1\",\n               \"t1-weighted\",\n               \"fat-saturated\"\n            ],\n            \"vendors\": [],\n            \"centres\": [\n               \"Duke Hospital\"\n            ],\n            \"function\": [],\n            \"condition\": [],\n            \"dataset\": [\n               \"DUKE\"\n            ],\n            \"augmentations\": [],\n            \"generates\": [],\n            \"height\": 512,\n            \"width\": 512,\n            \"depth\": 1,\n            \"type\": \"pix2pixHD\",\n            \"license\": \"BSD License\",\n            \"dataset_type\": \"DCE-MRI\",\n            \"privacy_preservation\": \"\",\n            \"tags\": [\n               \"dce-mri\",\n               \"postcontrast\",\n               \"synthesis\",\n               \"breast\",\n               \"mri\",\n               \"treatment\",\n               \"i2i\",\n               \"pix2pixHD\",\n               \"SPIE\"\n            ],\n            \"year\": 2023\n         },\n         \"description\": {\n            \"title\": \"Pre- to Post-Contrast Breast MRI Synthesis for Enhanced Tumour Segmentation\",\n            \"provided_date\": \"11.2023\",\n            \"trained_date\": \"2023\",\n            \"provided_after_epoch\": 30,\n            \"version\": \"1.0\",\n            \"publication\": \"https://doi.org/10.48550/arXiv.2311.10879\",\n            \"doi\": [\n               \"https://doi.org/10.48550/arXiv.2311.10879\"\n            ],\n            \"inputs\": [\n                \"input_path: default=input/, help=the path to .png breast DCE-MRI images that are translated from pre-contrast to the first DCE post-contrast sequence. \",\n                \"image_size: default=[512, 512], help=list with image height and width. \",\n                \"gpu_id: default=0, help=the gpu to run the model on.\"\n            ],\n            \"comment\": \"Pix2Pix model for DCE-MRI slice generation from pre-contrast image input (Trained on Duke Breast MRI Dataset). A pix2pixHD mmodel that generates DCE-MRI axial slices based on checkpoint after 30 training epochs. \\nThe pixel dimension of the generated images is 512x512. Several generated 2d slices can be merged together to create a 3D MRI volume with tumour tissue highlighted by synthetic contrast. \\nThe uploaded ZIP file contains the files 30_net_G.pth (model weights), __init__.py (image generation method and utils), a requirements.txt, and further code below the /src folder for handling of model, data, and training utils. Sample input images are provided as an example for image generation.\"\n         }\n  }\n}"
  },
  {
    "path": "docs/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nSPHINXPROJ    = medigan\nSOURCEDIR     = source\nBUILDDIR      = build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sphinx-build\r\n)\r\nset SOURCEDIR=source\r\nset BUILDDIR=build\r\nset SPHINXPROJ=medigan\r\n\r\nif \"%1\" == \"\" goto help\r\n\r\n%SPHINXBUILD% >NUL 2>NUL\r\nif errorlevel 9009 (\r\n\techo.\r\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\r\n\techo.installed, then set the SPHINXBUILD environment variable to point\r\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\r\n\techo.may add the Sphinx directory to PATH.\r\n\techo.\r\n\techo.If you don't have Sphinx installed, grab it from\r\n\techo.http://sphinx-doc.org/\r\n\texit /b 1\r\n)\r\n\r\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\ngoto end\r\n\r\n:help\r\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\n\r\n:end\r\npopd\r\n"
  },
  {
    "path": "docs/requirements.txt",
    "content": "# File: docs/requirements.txt\n\nsphinx-rtd-theme==1.0.0\nSphinx==4.5.0\nsphinx-automodapi==0.14.1\ntqdm==4.64.0\nrequests==2.31.0\ntorch==1.13.1\nPyGithub==1.55\nnumpy==1.22.4\nmatplotlib==3.4.3\nmyst-parser\n"
  },
  {
    "path": "docs/source/adding_models.rst",
    "content": "Model Contributions\n=====================\n\nWe are happy that you are considering contributing your model to `medigan`.\nThis will make your model accessible to the community and our users can easily integrate your synthetic data into their training pipelines and experiments.\n\n\nGuide: Automated Model Contribution\n_________________________________________\n\nCreate an `__init__.py <templates/examples/__init__.py>`_ file in your model's root folder.\n\nNext, run the following code to contribute your model to `medigan`.\n\n- Your model will be stored on `Zenodo <https://zenodo.org/>`_.\n\n- Also, a Github `issue <https://github.com/RichardObi/medigan/issues>`_ will be created to add your model's metadata to medigan's `global.json <https://github.com/RichardObi/medigan/blob/main/config/global.json>`_.\n\n- To do so, please provide a github access token (`get one here <https://github.com/settings/tokens>`_) and a zenodo access token (`get one here <https://zenodo.org/account/settings/applications/tokens/new/>`_), as shown below. After creation, the zenodo access token may take a few minutes before being recognized in zenodo API calls.\n\n.. code-block:: Python\n\n    from medigan import Generators\n    gen = Generators()\n\n    # Contribute your model\n    gen.contribute(\n        model_id = \"00100_YOUR_MODEL\", # assign an ID\n        init_py_path =\"path/ending/with/__init__.py\",\n        model_weights_name = \"10000\",\n        model_weights_extension = \".pt\",\n        generate_method_name = \"generate\", # in __init__.py\n        dependencies = [\"numpy\", \"torch\"],\n        creator_name = \"YOUR_NAME\",\n        creator_affiliation = \"YOUR_AFFILIATION\",\n        zenodo_access_token = 'ZENODO_ACCESS_TOKEN',\n        github_access_token = 'GITHUB_ACCESS_TOKEN',\n    )\n\nGuide:  Manual Model Contribution\n______________________________________\n\nIn the following, you find a step-by-step guide on how to contribute your generative model to `medigan`.\nIn case you encounter problems during this process feel free to reach out by creating an issue `here <https://github.com/RichardObi/medigan-models/issues>`_ and we will try to help.\nCheckout the figure below that shows the main components of the model contribution workflow depicted in yellow (d).\n\n.. figure:: _static/medigan-workflows.png\n   :alt: Architectural overview and main workflows\n\n   Architectural overview including main workflows consisting of (a) library import and initialisation, (b) generative model search and ranking, (c) sample generation, and (d) generative model contribution.\n\nIf you are here, you have recently developed a generative model such as a GAN, VAE, Diffusion Model, etc and you would like to boost your model's impact, reusability, dissemination by uploading it to `medigan`.\nWe are delighted and will assist you in adding your model.\n\n#. **Firstly, let's create the needed files:**\n\n    To add your model you will need the following files.\n\n    #. A checkpoint file that contains your trained model weights (e.g.,  the ``state_dict`` in pytorch)\n\n    #. An ``__init__.py`` file that contains functions that\n\n        - load the weights file (let's call that one ``weights.pt``)\n\n        - initialize your model with these weights\n\n        - generate samples with the initialized model.\n\n    #. Now that you have the ``weights.pt`` and the ``__init__.py``, let's check if we can make them work together.\n\n        #. Run your ``__init__.py``'s generate function using e.g. ``python -m __init__.py generate``\n\n        #. Check whether your model did load the weights effectively and whether synthetic samples were generated as expected.\n\n    #. Apply some necessary adjustments to your model package, particularly to your ``__init__.py``:\n\n        #. We have some templates that you can use to guide the adjustments described below\n\n            - If you are using a model that generates samples **without** image input (e.g., noise-to-image): Download Model `00002 <https://doi.org/10.5281/zenodo.5188557>`_ from `here <https://zenodo.org/record/5548158/files/MALIGN_DCGAN.zip?download=1>`_. Unzip it and open the ``__init__.py`` that contains an example ``generate()`` method.\n\n            - If you are using a model that generates samples **with** image input (e.g., image-to-image): Download Model `00003 <https://doi.org/10.5281/zenodo.5547263>`_ from `here <https://zenodo.org/record/5555010/files/CycleGAN_high_density.zip?download=1>`_. Unzip it and open the ``__init__.py`` that contains an example ``generate()`` method.\n\n        #. Please note that user's of `medigan` models may add your model to their preprocessing or AI training pipelines. Please make sure that your model, hence, runs efficiently. For instance, your model should load the weights only once even though the generate() function is called multiple times.\n\n        #. Please make sure your model is able to run both on gpu and on cpu and your code automatically detects on which one to run.\n\n        #. Please replace all ``print()`` functions from your code with ``logging.debug()`` (for this you need to ``import logging``).\n\n        #. Please add appropriate error handling using ``try, except`` blocks on each possible source of error in your code. ``raise`` the error in your ``generate()`` function to allow `medigan` to handle it and pass it to the user.\n\n        #. If your generative model needs some input images, provide a few example images in a folder called ``/images``. Users may test your model with these example images before feeding their own input images to your model.\n\n        #. There are a few parameters of the ``generate()`` that are mandatory in `medigan` and others that you can set optionally.\n\n            - Mandatory:\n                - ``model_file``: string, the path where your ``generate()`` method will find its weight file\n                - ``output_path``: string, the path where our ``generate()`` method should store the generated images\n                - ``save_images``: boolean, whether your ``generate()`` method should store generated samples in ``output_path`` or return them as numpy arrays.\n                - ``num_samples``: int, the number of samples that should be generated.\n\n            - Optional:\n                - ``input_path``: string, the path where our ``generate()`` method finds images that should be used as input into the generative model (i.e. in image-to-image translation).\n                - ``image_size``: array, that contains image height, width, and, optionally, also depth.\n                - ``translate_all_images``: boolean, in image-to-image translation, if ``True``, this overwrites the ``num_samples`` and instead translates all images found in ``input_path``.\n                - ``gpu_id``: int, if a user has various GPUs available, the user can specify which one of them to use to run your generative model.\n\n#. **Secondly, test your model locally:**\n    Okay, now that we know which files we need, let's test them using a local version of `medigan`.\n\n    #. Let's start by cloning `medigan` e.g. using the command line: ``git clone https://github.com/RichardObi/medigan.git``\n    #. Next, cd into `medigan`, install the dependencies of `medigan`, and create a virtual environment.\n\n        You can do so running these commands:\n\n        - ``cd medigan``\n        - ``pip install pipenv``\n        - ``pipenv install``\n        - ``pipenv shell``\n\n    #. Now that you have your environment up and running, please run the following command to download the config file.\n\n        - ``python -m tests.tests TestMediganMethods.test_init_generators``\n\n    #. In the folder ``/config``, you should now see a file called `global.json <https://raw.githubusercontent.com/RichardObi/medigan-models/main/global.json>`_. In this file each model's metadata is stored.\n\n        * Please add the metadata for your model at the bottom of the `global.json` file.\n        * To add the metadata, you can use the metadata of model `00001 <https://doi.org/10.5281/zenodo.5187714>`_ in `global.json <https://raw.githubusercontent.com/RichardObi/medigan-models/main/global.json>`_ as example.\n        * Copy the metadata of model 00001 and add it to the bottom of ``global.json``. Then adjust each entry in that part of the json so that it represents your own model.\n            - The ``model_id`` should follow the convention ``NNNNN_TTTTTT_MMMM_AAAAA_GGGG`` (N = Number of model, T = Type of model, M = Modality, A = Anatomic/Ailment Information, G = Generated Sample Type information i.e. full for full image or roi for region of interest)\n            - The field ``package_link`` (under ``execution``) should point to a local zip file ``NAME_OF_YOUR_MODEL_PACKAGE.zip`` of your model package.\n            - json entries below ``execution`` are important and needed to run the model in `medigan`, e.g. the name and parameters of a ``generate()`` function in the ``__init__.py``\n            - json entries below ``selection`` are important to enable users to search and rank the model compared to other models in `medigan`, e.g. the performance indicators such as SSIM, MSE, PSNR, etc.\n            - json entries below ``description`` are to allow tracing back the origin and metadata of the model and allow users to get further information about the model, e.g. license, related publications, etc.\n\n    #. You are almost done! It's Testing Time!\n\n        - Run a local test using the following code:\n\n        .. code-block:: Python\n\n            from medigan import Generators\n            gen = Generators()\n            gen.generate(model_id=\"YOUR_MODEL_ID\")\n\n            # Test a few variations.\n            test_dict = {\"translate_all_images\": True, \"SOME_OTHER_OPTIONAL_PARAMS\": True}\n            gen.generate(model_id=\"YOUR_MODEL_ID\", num_samples=100, output_path=\"here\", save_images=True, **test_dict)\n\n        - If you are code runs well with different settings/params, congratulations, you have made it! You integrated your model as a package into `medigan` and are now ready for the final steps.\n\n#. **Thirdly, upload your model:**\n\n    #. Package and upload your model to Zenodo - home to your model's code and documentation.\n\n        #. First, check if your model package folder contains an ``__init__.py``, a ``weights`` file, a ``license`` file, and optionally other files.\n\n        #. The next step is to zip this folder. To do so (e.g., on MACOS) you may ``cd`` into the folder and use the following commands (while removing hidden OS system files):\n\n            .. code-block:: Python\n\n                find . -name \".DS_Store\" -delete\n                zip -r NAME_OF_YOUR_MODEL_PACKAGE.zip . -x \".*\" -x \"__MACOSX\"\n\n        Now that you have your model package zipped and ready, note that `medigan` model's are commonly stored in Zenodo, as in Zenodo\n            * they get a DOI\n            * the content of their package is non editable i.e. no file modifications/updates without new DOI.\n            * This helps to avoid security issues as package content remains static after the model is tested, verified, and added to `medigan`.\n            * Zenodo has a close to unlimited storage capacity for research data/software.\n            * Also, the authorship/ownership of the model are clear\n            * There is transparent licensing.\n            * Each model is versioned in Zenodo with different DOIs.\n            * A model documentation and contact information can be added.\n\n        #. Checkout this example of our model `00001 <https://doi.org/10.5281/zenodo.5187714>`_ on Zenodo. You can use the Zenodo documentation of this model as template for your own model upload.\n\n        #. Now, let's go to the `Zenodo <https://zenodo.org/>`_ website.\n\n        #. Click on ``New Upload`` (if you don't have an account, you can quickly create one e.g., using your `ORCID <https://orcid.org/>`_)\n\n        #. Fill in the metadata fields for your model and upload the model package zip file (i.e. drag and drop).\n\n        #. Click on ``Save`` and ``Submit``. Congratulations your model is now on Zenodo! Good job!\n\n#. **Finally, add your model to `medigan's` model metadata:**\n\n        Last step!!! Your model is on Zenodo and tested locally. Now we can officially add it to `medigan`. Remember the ``global.json`` that you created locally to test your model? It is time for glory for this file.\n\n        #. Now, clone the `medigan-models` repository (the home of `medigan's global.json <https://github.com/RichardObi/medigan-models/blob/main/global.json>`_) e.g. by using ``git clone https://github.com/RichardObi/medigan-models.git``\n\n        #. Create and checkout a new local branch ``git checkout -b mynewbranch``\n\n        #. Open the ``global.json`` in your cloned local `medigan-models`\n\n        #. Edit the ``global.json`` file and add your model's entry at the bottom, and save.\n\n        #. Note that this is the time to replace the value of ``package_link`` from your local model file path to your new Zenodo model URL. To get this URL, go to the Zenodo page of your model, and scroll down to ``Files``, where you see a download button. Copy the url link that this button points to, which is your ``package_link``.\n\n        #. Commit the new file (``git add .``, ``git commit -m \"added model YOUR_MODEL_ID.\"``) and push your branch (``git push``).\n\n        #. Lastly, go to the repository `medigan-models <https://github.com/RichardObi/medigan-models/>`_ and create a pull request that merges your recently pushed branch into ``main``.\n\n        #. That's it!!! Your pull request will be evaluated asap. Once approved your model is officially part of `medigan`!\n\nIf you have suggestions on improvements for our model contribution process, please take a minute and let us know `here <https://github.com/RichardObi/medigan-models/issues>`_.\n\n\nConventions that your model should follow\n______________________________________________\n\n* Your model should have a ``generate`` method with the params model_file:str, num_samples:int, save_images:bool, and output_path:str (see `template (templates/examples/__init__.py>`_)\n* Also, the model should do simple error handling, run flexibly on either gpu or cpu, use ``logging`` instead of ``prints``, and create some sort of synthetic data.\n\nWe hope to welcome you model soon to `medigan`! If you need support, please let us now `here <https://github.com/RichardObi/medigan/issues>`_."
  },
  {
    "path": "docs/source/api/medigan.config_manager.ConfigManager.rst",
    "content": "ConfigManager\n=============\n\n.. currentmodule:: medigan.config_manager\n\n.. autoclass:: ConfigManager\n   :show-inheritance:\n\n   .. rubric:: Methods Summary\n\n   .. autosummary::\n\n      ~ConfigManager.add_model_to_config\n      ~ConfigManager.get_config_by_id\n      ~ConfigManager.is_model_in_config\n      ~ConfigManager.is_model_metadata_valid\n      ~ConfigManager.load_config_file\n\n   .. rubric:: Methods Documentation\n\n   .. automethod:: add_model_to_config\n   .. automethod:: get_config_by_id\n   .. automethod:: is_model_in_config\n   .. automethod:: is_model_metadata_valid\n   .. automethod:: load_config_file\n"
  },
  {
    "path": "docs/source/api/medigan.contribute_model.model_contributor.ModelContributor.rst",
    "content": "ModelContributor\n================\n\n.. currentmodule:: medigan.contribute_model.model_contributor\n\n.. autoclass:: ModelContributor\n   :show-inheritance:\n\n   .. rubric:: Methods Summary\n\n   .. autosummary::\n\n      ~ModelContributor.add_metadata_from_file\n      ~ModelContributor.add_metadata_from_input\n      ~ModelContributor.is_value_for_key_already_set\n      ~ModelContributor.load_metadata_template\n      ~ModelContributor.push_to_github\n      ~ModelContributor.push_to_zenodo\n      ~ModelContributor.validate_and_update_model_weights_path\n      ~ModelContributor.validate_init_py_path\n      ~ModelContributor.validate_local_model_import\n      ~ModelContributor.validate_model_id\n\n   .. rubric:: Methods Documentation\n\n   .. automethod:: add_metadata_from_file\n   .. automethod:: add_metadata_from_input\n   .. automethod:: is_value_for_key_already_set\n   .. automethod:: load_metadata_template\n   .. automethod:: push_to_github\n   .. automethod:: push_to_zenodo\n   .. automethod:: validate_and_update_model_weights_path\n   .. automethod:: validate_init_py_path\n   .. automethod:: validate_local_model_import\n   .. automethod:: validate_model_id\n"
  },
  {
    "path": "docs/source/api/medigan.contribute_model.rst",
    "content": "medigan.contribute\\_model package\n=================================\n\nSubmodules\n----------\n\nmedigan.contribute\\_model.base\\_model\\_uploader module\n------------------------------------------------------\n\n.. automodule:: medigan.contribute_model.base_model_uploader\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.contribute\\_model.github\\_model\\_uploader module\n--------------------------------------------------------\n\n.. automodule:: medigan.contribute_model.github_model_uploader\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.contribute\\_model.model\\_contributor module\n---------------------------------------------------\n\n.. automodule:: medigan.contribute_model.model_contributor\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.contribute\\_model.zenodo\\_model\\_uploader module\n--------------------------------------------------------\n\n.. automodule:: medigan.contribute_model.zenodo_model_uploader\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nModule contents\n---------------\n\n.. automodule:: medigan.contribute_model\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/api/medigan.execute_model.model_executor.ModelExecutor.rst",
    "content": "ModelExecutor\n=============\n\n.. currentmodule:: medigan.execute_model.model_executor\n\n.. autoclass:: ModelExecutor\n   :show-inheritance:\n\n   .. rubric:: Methods Summary\n\n   .. autosummary::\n\n      ~ModelExecutor.generate\n      ~ModelExecutor.is_model_already_unpacked\n\n   .. rubric:: Methods Documentation\n\n   .. automethod:: generate\n   .. automethod:: is_model_already_unpacked\n"
  },
  {
    "path": "docs/source/api/medigan.execute_model.rst",
    "content": "medigan.execute\\_model package\n==============================\n\nSubmodules\n----------\n\nmedigan.execute\\_model.install\\_model\\_dependencies module\n----------------------------------------------------------\n\n.. automodule:: medigan.execute_model.install_model_dependencies\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.execute\\_model.model\\_executor module\n---------------------------------------------\n\n.. automodule:: medigan.execute_model.model_executor\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.execute\\_model.synthetic\\_dataset module\n------------------------------------------------\n\n.. automodule:: medigan.execute_model.synthetic_dataset\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nModule contents\n---------------\n\n.. automodule:: medigan.execute_model\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/api/medigan.generators.Generators.rst",
    "content": "Generators\n==========\n\n.. currentmodule:: medigan.generators\n\n.. autoclass:: Generators\n   :show-inheritance:\n\n   .. rubric:: Methods Summary\n\n   .. autosummary::\n\n      ~Generators.add_all_model_executors\n      ~Generators.add_metadata_from_file\n      ~Generators.add_metadata_from_input\n      ~Generators.add_model_contributor\n      ~Generators.add_model_executor\n      ~Generators.add_model_to_config\n      ~Generators.contribute\n      ~Generators.find_matching_models_by_values\n      ~Generators.find_model_and_generate\n      ~Generators.find_model_executor_by_id\n      ~Generators.find_models_and_rank\n      ~Generators.find_models_rank_and_generate\n      ~Generators.generate\n      ~Generators.get_as_torch_dataloader\n      ~Generators.get_as_torch_dataset\n      ~Generators.get_config_by_id\n      ~Generators.get_generate_function\n      ~Generators.get_model_contributor_by_id\n      ~Generators.get_model_executor\n      ~Generators.get_models_by_key_value_pair\n      ~Generators.get_selection_criteria_by_id\n      ~Generators.get_selection_criteria_by_ids\n      ~Generators.get_selection_keys\n      ~Generators.get_selection_values_for_key\n      ~Generators.is_model_executor_already_added\n      ~Generators.is_model_metadata_valid\n      ~Generators.list_models\n      ~Generators.push_to_github\n      ~Generators.push_to_zenodo\n      ~Generators.rank_models_by_performance\n      ~Generators.test_model\n      ~Generators.visualize\n\n   .. rubric:: Methods Documentation\n\n   .. automethod:: add_all_model_executors\n   .. automethod:: add_metadata_from_file\n   .. automethod:: add_metadata_from_input\n   .. automethod:: add_model_contributor\n   .. automethod:: add_model_executor\n   .. automethod:: add_model_to_config\n   .. automethod:: contribute\n   .. automethod:: find_matching_models_by_values\n   .. automethod:: find_model_and_generate\n   .. automethod:: find_model_executor_by_id\n   .. automethod:: find_models_and_rank\n   .. automethod:: find_models_rank_and_generate\n   .. automethod:: generate\n   .. automethod:: get_as_torch_dataloader\n   .. automethod:: get_as_torch_dataset\n   .. automethod:: get_config_by_id\n   .. automethod:: get_generate_function\n   .. automethod:: get_model_contributor_by_id\n   .. automethod:: get_model_executor\n   .. automethod:: get_models_by_key_value_pair\n   .. automethod:: get_selection_criteria_by_id\n   .. automethod:: get_selection_criteria_by_ids\n   .. automethod:: get_selection_keys\n   .. automethod:: get_selection_values_for_key\n   .. automethod:: is_model_executor_already_added\n   .. automethod:: is_model_metadata_valid\n   .. automethod:: list_models\n   .. automethod:: push_to_github\n   .. automethod:: push_to_zenodo\n   .. automethod:: rank_models_by_performance\n   .. automethod:: test_model\n   .. automethod:: visualize\n"
  },
  {
    "path": "docs/source/api/medigan.model_visualizer.ModelVisualizer.rst",
    "content": "ModelVisualizer\n===============\n\n.. currentmodule:: medigan.model_visualizer\n\n.. autoclass:: ModelVisualizer\n   :show-inheritance:\n\n   .. rubric:: Methods Summary\n\n   .. autosummary::\n\n      ~ModelVisualizer.visualize\n\n   .. rubric:: Methods Documentation\n\n   .. automethod:: visualize\n"
  },
  {
    "path": "docs/source/api/medigan.rst",
    "content": "medigan package\n===============\n\nSubpackages\n-----------\n\n.. toctree::\n   :maxdepth: 4\n\n   medigan.contribute_model\n   medigan.execute_model\n   medigan.select_model\n\nSubmodules\n----------\n\nmedigan.config\\_manager module\n------------------------------\n\n.. automodule:: medigan.config_manager\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.constants module\n------------------------\n\n.. automodule:: medigan.constants\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.exceptions module\n-------------------------\n\n.. automodule:: medigan.exceptions\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.generators module\n-------------------------\n\n.. automodule:: medigan.generators\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.model\\_visualizer module\n--------------------------------\n\n.. automodule:: medigan.model_visualizer\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.utils module\n--------------------\n\n.. automodule:: medigan.utils\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nModule contents\n---------------\n\n.. automodule:: medigan\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/api/medigan.select_model.model_selector.ModelSelector.rst",
    "content": "ModelSelector\n=============\n\n.. currentmodule:: medigan.select_model.model_selector\n\n.. autoclass:: ModelSelector\n   :show-inheritance:\n\n   .. rubric:: Methods Summary\n\n   .. autosummary::\n\n      ~ModelSelector.find_matching_models_by_values\n      ~ModelSelector.find_models_and_rank\n      ~ModelSelector.get_models_by_key_value_pair\n      ~ModelSelector.get_selection_criteria_by_id\n      ~ModelSelector.get_selection_criteria_by_ids\n      ~ModelSelector.get_selection_keys\n      ~ModelSelector.get_selection_values_for_key\n      ~ModelSelector.rank_models_by_performance\n      ~ModelSelector.recursive_search_for_values\n\n   .. rubric:: Methods Documentation\n\n   .. automethod:: find_matching_models_by_values\n   .. automethod:: find_models_and_rank\n   .. automethod:: get_models_by_key_value_pair\n   .. automethod:: get_selection_criteria_by_id\n   .. automethod:: get_selection_criteria_by_ids\n   .. automethod:: get_selection_keys\n   .. automethod:: get_selection_values_for_key\n   .. automethod:: rank_models_by_performance\n   .. automethod:: recursive_search_for_values\n"
  },
  {
    "path": "docs/source/api/medigan.select_model.rst",
    "content": "medigan.select\\_model package\n=============================\n\nSubmodules\n----------\n\nmedigan.select\\_model.matched\\_entry module\n-------------------------------------------\n\n.. automodule:: medigan.select_model.matched_entry\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.select\\_model.model\\_match\\_candidate module\n----------------------------------------------------\n\n.. automodule:: medigan.select_model.model_match_candidate\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nmedigan.select\\_model.model\\_selector module\n--------------------------------------------\n\n.. automodule:: medigan.select_model.model_selector\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nModule contents\n---------------\n\n.. automodule:: medigan.select_model\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/api/modules.rst",
    "content": "src\n===\n\n.. toctree::\n   :maxdepth: 4\n\n   medigan\n"
  },
  {
    "path": "docs/source/code_doc.rst",
    "content": "Full Code Documentation\n=========================\n\n.. toctree::\n   :maxdepth: 5\n\n   api/medigan"
  },
  {
    "path": "docs/source/code_examples.rst",
    "content": "Code Examples\n==============\n\n.. contents:: Table of Contents\n\nInstall\n__________________________\nInstall `medigan` library from pypi (or github).\n\n.. code-block:: Python\n\n    pip install medigan\n\nImport `medigan` and initialize Generators\n\n.. code-block:: Python\n\n    from medigan import Generators\n    generators = Generators()\n\n\nGenerate Images\n_______________________________________\nGenerate 10 samples using one (model 1 is `00001_DCGAN_MMG_CALC_ROI`) of the `medigan models <https://doi.org/10.5281/zenodo.5187714>`_ from the `config <https://github.com/RichardObi/medigan-models/blob/main/global.json>`_.\n\n`install_dependencies` signals to medigan that the user wishes to automatically install all the python dependencies (e.g. numpy. torch, etc) required to run this model (i.e. to the user's active python environment).\n\n.. code-block:: Python\n\n    generators.generate(model_id=1, num_samples=10, install_dependencies=True)\n\nGet the model's generate method and run it to generate 3 samples\n\n.. code-block:: Python\n\n    # model 1 is \"00001_DCGAN_MMG_CALC_ROI\"\n    gen_function = generators.get_generate_function(model_id=1, num_samples=3)\n    gen_function()\n\nGet the model's synthetic data as torch dataloader with 3 samples\n\n.. code-block:: Python\n\n    # model 4 is \"00004_PIX2PIX_MMG_MASSES_W_MASKS\"\n    dataloader = generators.get_as_torch_dataloader(model_id=4, num_samples=3)\n\n\nVisualize Generative Model\n_______________________________________\n\nDisplays an interactive visual interface for exploration of applicable models.\n\n.. code-block:: Python\n\n    # model 10 is \"00010_FASTGAN_POLYP_PATCHES_W_MASKS\"\n    generators.visualize(10)\n\n.. figure:: _static/interface.png\n   :alt: Visualization example for model 00010\n\nSearch for Generative Models\n_______________________________________\nFind all models that contain a specific key-value pair in their model config.\n\n.. code-block:: Python\n\n    key = \"modality\"\n    value = \"Full-Field Mammography\"\n    found_models = generators.get_models_by_key_value_pair(key1=key, value1=value, is_case_sensitive=False)\n    print(found_models)\n\nCreate a list of search terms and find the models that have these terms in their config.\n\n.. code-block:: Python\n\n    values_list = ['dcgan', 'Mammography', 'inbreast']\n    models = generators.find_matching_models_by_values(values=values_list, target_values_operator='AND', are_keys_also_matched=True, is_case_sensitive=False)\n    print(f'Found models: {models}')\n\nCreate a list of search terms, find a model and generate\n\n.. code-block:: Python\n\n    values_list = ['dcgan', 'mMg', 'ClF', 'modalities', 'inbreast']\n    generators.find_model_and_generate(values=values_list, target_values_operator='AND', are_keys_also_matched=True, is_case_sensitive=False, num_samples=5)\n\nRank Generative Models\n_______________________________________\nRank the models by a performance metric and return ranked list of models\n\n.. code-block:: Python\n\n    ranked_models = generators.rank_models_by_performance(metric=\"SSIM\", order=\"asc\")\n    print(ranked_models)\n\nFind the models, then rank them by a performance metric and return ranked list of models\n\n.. code-block:: Python\n\n    ranked_models = generators.find_models_and_rank(values=values_list, target_values_operator='AND', are_keys_also_matched=True, is_case_sensitive=False, metric=\"SSIM\", order=\"asc\")\n    print(ranked_models)\n\nFind the models, then rank them, and then generate samples with the best ranked model.\n\n.. code-block:: Python\n\n    generators.find_models_rank_and_generate(values=values_list, target_values_operator='AND', are_keys_also_matched=True, is_case_sensitive=False, metric=\"SSIM\", order=\"asc\", num_samples=5)\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# Configuration file for the Sphinx documentation builder.\n#\n# This file does only contain a selection of the most common options. For a\n# full list see the documentation:\n# http://www.sphinx-doc.org/en/master/config\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\nimport sys\n\nsys.path.insert(0, os.path.abspath(\"../../src\"))\n\n# -- Project information -----------------------------------------------------\n\nproject = \"medigan\"\ncopyright = \"2022, Richard Osuala, Grzegorz Skorupko, Noussair Lazrak\"\nauthor = \"Richard Osuala, Grzegorz Skorupko, Noussair Lazrak\"\n\n# The short X.Y version\nversion = \"\"\n# The full version, including alpha/beta/rc tags\nrelease = \"1.0.0\"\n\n# -- General configuration ---------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\n\nextensions = [\n    \"sphinx.ext.autodoc\",\n    \"sphinx.ext.doctest\",\n    \"sphinx.ext.coverage\",\n    \"sphinx.ext.mathjax\",\n    \"sphinx.ext.viewcode\",\n    \"sphinx.ext.githubpages\",\n    \"sphinx.ext.napoleon\",\n    \"sphinx_rtd_theme\",\n    \"sphinx_automodapi.automodapi\",\n    \"sphinx_automodapi.smart_resolver\",\n    \"sphinx.ext.autosummary\",\n    \"myst_parser\",\n]\n# 'sphinx_automodapi.smart_resolver'\n\n# https://sphinx-automodapi.readthedocs.io/_/downloads/en/stable/pdf/\nnumpydoc_show_class_members = False\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = [\"_templates\"]\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = \".rst\"\n\n# The master toctree document.\nmaster_doc = \"index\"\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path .\nexclude_patterns = []\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = \"sphinx\"\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n# Check themes here: https://sphinxthemes.com/\nhtml_theme = \"sphinx_rtd_theme\"  # 'book' #'classic' #'alabaster'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\nhtml_theme_options = {\n    \"collapse_navigation\": True,\n    \"navigation_depth\": 5,\n    \"includehidden\": True,\n    \"titles_only\": False,\n}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = [\"_static\"]\n\n# Custom sidebar templates, must be a dictionary that maps document names\n# to template names.\n#\n# The default sidebars (for documents that don't match any pattern) are\n# defined by theme itself.  Builtin themes are using these templates by\n# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',\n# 'searchbox.html']``.\n#\n# html_sidebars = {}\n\n\n# -- Options for HTMLHelp output ---------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = \"medigandoc\"\n\n# -- Options for LaTeX output ------------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, \"medigan.tex\", \"medigan Documentation\", [author], \"manual\"),\n]\n\n# -- Options for manual page output ------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [(master_doc, \"medigan\", \"medigan Documentation\", [author], 1)]\n\n# -- Options for Texinfo output ----------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (\n        master_doc,\n        \"medigan\",\n        \"medigan Documentation\",\n        author,\n        \"medigan\",\n        \"One line description of project.\",\n        \"Miscellaneous\",\n    ),\n]\n\n# -- Options for Epub output -------------------------------------------------\n\n# Bibliographic Dublin Core info.\nepub_title = project\nepub_author = author\nepub_publisher = author\nepub_copyright = copyright\n\n# The unique identifier of the text. This can be a ISBN number\n# or the project homepage.\n#\n# epub_identifier = ''\n\n# A unique identification for the text.\n#\n# epub_uid = ''\n\n# A list of files that should not be packed into the epub file.\nepub_exclude_files = [\"search.html\"]\n\n# -- Extension configuration -------------------------------------------------\n"
  },
  {
    "path": "docs/source/description.rst",
    "content": "Description\n==============\n\nWelcome to `medigan`: **medi**\\ cal  **g**\\ enerative (\\ **a**\\ dversarial) **n**\\ etworks\n\n.. contents:: Table of Contents\n\n\nAim and Scope\n_______________\n\n`medigan` focuses on automating medical image dataset synthesis using GANs.\n\nThese datasets can again be used to train diagnostic or prognostic clinical models such as disease classification, detection and segmentation models.\n\nDespite this current focus, medigan, is readily extendable to any type of modality and any type of generative model.\n\n.. note::\n    More detail is available in the `medigan` article now available as `preprint <https://arxiv.org/abs/2209.14472>`_.\n    The article also includes experiments with insights into FID as generative model evaluation metric.\n\n\nCore Features\n_______________\n\n    - Researchers and ML-practitioners can conveniently use an existing model in `medigan` for synthetic data augmentation instead of having to train their own generative model each time.\n\n    - Users can search and find a model using search terms (e.g. \"Mammography, 128x128, DCGAN\") or key value pairs (e.g. `key` = \"modality\", `value` = \"Mammography\")\n\n    - Users can explore the config and information (metrics, use-cases, modalities, etc) of each model in `medigan`\n\n    - Users can generate samples using a model\n\n    - Users can also get the generate_method of a model that they may want to use dynamically inside their dataloaders\n\n    - Model contributors can share and disseminate their generative models thereby augmenting their reach.\n\n\nArchitecture and Workflows\n___________________________\n\n.. figure:: _static/medigan-workflows.png\n   :alt: Architectural overview and main workflows\n\n   Architectural overview including main workflows consisting of (a) library import and initialisation, (b) generative model search and ranking, (c) sample generation, and (d) generative model contribution.\n\nNotes\n_______________\n    - Each model in `medigan` has its own dependencies listed in the `global.json <https://github.com/RichardObi/medigan-models/blob/main/global.json>`_ model metadata/config file. Setting :code:`install_dependencies=True`, in the :code:`generate()` and related methods, triggers an automatic installation of the respective model's python dependencies (e.g. numpy. torch, etc), i.e., to the user's active python environment.\n    - Running the :code:`generate()` and related methods for synthetic data generation will trigger the download of the respective generative model to the user's local directory from where the code is run (i.e. the current CLI path).\n\nIssues\n_______________\nIn case you encounter problems while using `medigan` or would like to request additional features, please create a `new issue <https://github.com/RichardObi/medigan/issues>`_ and we will try to help.\n\n\nLinks\n___________________________\n- `Github (medigan library) <https://github.com/RichardObi/medigan>`_\n- `Test Pypi (medigan library) <https://test.pypi.org/project/medigan/>`_"
  },
  {
    "path": "docs/source/index.rst",
    "content": "\nGetting started\n=========================\nWelcome to `medigan`: **medi**\\ cal  **g**\\ enerative (\\ **a**\\ dversarial) **n**\\ etworks\n\nLet's install `medigan` and generate a few synthetic images.\n\n.. code-block:: Python\n\n    pip install medigan\n\n\n.. code-block:: Python\n\n    from medigan import Generators\n\n    # model 1 is \"00001_DCGAN_MMG_CALC_ROI\"\n    Generators().generate(model_id=1, install_dependencies=True)\n\n\nOverview\n===================\n.. figure:: _static/overview.png\n   :alt: Overview\n\n   Overview of `medigan` users, library, and models on the example of mammography cancer image dataset generation.\n\n.. note::\n    Check out our `article <https://arxiv.org/abs/2209.14472>`_ on `medigan` to get all the details.\n\n.. toctree::\n   :caption: Description\n   :maxdepth: 3\n\n   description\n\n.. toctree::\n   :caption: Models\n   :maxdepth: 3\n\n   models\n\n.. toctree::\n   :caption: Getting Started\n   :maxdepth: 3\n\n   code_examples\n\n.. toctree::\n   :caption: API Reference\n   :maxdepth: 2\n\n   modules_overview\n   code_doc\n\n.. toctree::\n   :caption: Tests\n   :maxdepth: 5\n\n   tests\n\n\n.. toctree::\n   :caption: Add Your Model\n   :maxdepth: 3\n\n   adding_models\n\nIndices\n=======\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`"
  },
  {
    "path": "docs/source/model_documentation.md",
    "content": "\n\n## 00001_DCGAN_MMG_CALC_ROI\n\nDCGAN Model for Mammogram Calcification Region of Interest Generation (Trained on INbreast).  \\\n<sub> **Note:** A deep convolutional generative adversarial network (DCGAN) that generates regions of interest (ROI) of mammograms containing benign and/or malignant calcifications. Pixel dimensions are 128x128. The DCGAN was trained on ROIs from the INbreast dataset (Moreira et al, 2012). The uploaded ZIP file contains the files dcgan.pt (model weights), __init__.py (image generation method and utils), a README.md, and the GAN model architecture (in pytorch) below the /src folder. Kernel size=6 used in DCGAN discriminator. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Calcification         |   mammography   |     dcgan      |  128x128  |   [Inbreast](https://www.academicradiology.org/article/S1076-6332(11)00451-X/fulltext)    | ![sample](_static/samples/00001.png) |  `00001_DCGAN_MMG_CALC_ROI`  | [Zenodo (5187714)](https://doi.org/10.5281/zenodo.5187714)  | | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(model_id=\"00001_DCGAN_MMG_CALC_ROI\", image_size=128)\n\n# model specific parameters\ninputs= [\"image_size: default=128, help=128 is the image size that works with the supplied checkpoint.\"]\n```\n\n\n\n## 00002_DCGAN_MMG_MASS_ROI\n\nDCGAN Model for Mammogram Mass Region of Interest Generation (Trained on OPTIMAM)  \\\n<sub> **Note:** A deep convolutional generative adversarial network (DCGAN) that generates regions of interest (ROI) of mammograms containing benign and/or malignant masses. Pixel dimensions are 128x128. The DCGAN was trained on ROIs from the Optimam dataset (Halling-Brown et al, 2014). The uploaded ZIP file contains the files malign_mass_gen (model weights), and __init__.py (image generation method and pytorch GAN model architecture). Kernel size=6 used in DCGAN discriminator. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Mass                  |   mammography   |     dcgan      |  128x128  |    [Optimam](https://doi.org/10.48550/arXiv.2004.04742)    | ![sample](_static/samples/00002.png) |  `00002_DCGAN_MMG_MASS_ROI`  | [Zenodo (5188557)](https://doi.org/10.5281/zenodo.5188557)  | [Alyafi et al (2019)](https://doi.org/10.48550/arXiv.1909.02062)  | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(model_id=\"00002_DCGAN_MMG_MASS_ROI\")\n\n# model specific parameters\ninputs= []\n```\n\n# 00003_CYCLEGAN_MMG_DENSITY_FULL\n\nCycleGAN Model for Low-to-High Brest Density Mammograms Translation (Trained on BCDR)  \\\n<sub> **Note:** A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from BCDR dataset (Lopez, M. G., et al. 2012). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Density Transfer      |   mammography   |    cyclegan    | 1332x800  |     [BCDR](https://bcdr.eu/information/about)      | ![sample](_static/samples/00003.png) |  `00003_CYCLEGAN_MMG_DENSITY_FULL`  | [Zenodo (5547263)](https://doi.org/10.5281/zenodo.5547263)  | [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00003_CYCLEGAN_MMG_DENSITY_FULL\",\n    input_path=\"models/00003_CYCLEGAN_MMG_DENSITY_FULL/images\",\n    image_size=[1332, 800],\n    gpu_id=0,\n)\n\n# model specific parameters\ninputs= [\n    \"input_path: default=models/00003_CYCLEGAN_MMG_DENSITY_FULL/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n    \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n    \"gpu_id: default=0, help=the gpu to run the model on.\",\n    \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\"\n]\n```\n\n# 00004_PIX2PIX_MMG_MASSES_W_MASKS\n\nGenerates synthetic patches given a random mask tiled with texture patches extracted from real images (Trained on BCDR)  \\\n<sub> **Note:** Generates synthetic patches given a random mask tiled with texture patches extracted from real images. The texture patches should be extracted from within the mass an outside the mass of a real image. Hence, some real ROIs are required to start with and its ideal for data augmentation purposes for mass segmentation. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Mass with Mask        |   mammography   |    pix2pix     |  256x256  |     [BCDR](https://bcdr.eu/information/about)      | ![sample](_static/samples/00004.png) <br> ![sample](_static/samples/00004_mask.png) |  `00004_PIX2PIX_MMG_MASSES_W_MASKS`  | [Zenodo (7093759)](https://doi.org/10.5281/zenodo.7093759)  |  | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00004_PIX2PIX_MMG_MASSES_W_MASKS\",\n    input_path=\"models/00004_PIX2PIX_MMG_MASSES_W_MASKS/images\",\n    image_size=[256, 256],\n    patch_size=[32, 32],\n    shapes=['oval', 'lobulated'],\n    ssim_threshold=0.2,\n    gpu_id=0,\n)\n\n# model specific parameters\ninputs= [\n    \"input_path: default=models/00004_PIX2PIX_MMG_MASSES_W_MASKS/images help=inputs that are used in the pix2pix input image pool (e.g. for tiled image generation) \",\n    \"image_size: default=[256, 256] help=height and width of images.\",\n    \"patch_size: default=[32, 32] help=height and width of patches (annotation size on image).\",\n    \"shapes: default=['oval', 'lobulated'] help=the type of the mask curve shapes generated via bezier curves.\",\n    \"ssim_threshold: default=0.2, help=the SSIM threshold that images must surpass to be output.\",\n    \"gpu_id: default=0 help=the gpu to run the model.\",\n]\n```\n\n\n# 00005_DCGAN_MMG_MASS_ROI\n\nDCGAN Model for Mammogram MASS Patch Generation (Trained on BCDR)  \\\n<sub> **Note:** A deep convolutional generative adversarial network (DCGAN) that generates mass patches of mammograms. Pixel dimensions are 128x128. The DCGAN was trained on MMG patches from the BCDR dataset (Lopez et al, 2012). The uploaded ZIP file contains the files 500.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, and the GAN model architecture (in pytorch) below the /src folder. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Mass                  |   mammography   |     dcgan      |  128x128  |     [BCDR](https://bcdr.eu/information/about)      | ![sample](_static/samples/00005.png) |  `00005_DCGAN_MMG_MASS_ROI`  | [Zenodo (6555188)](https://doi.org/10.5281/zenodo.6555188)  | [Szafranowska et al (2022)](https://doi.org/10.48550/arXiv.2203.04961)  | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(model_id=\"00005_DCGAN_MMG_MASS_ROI\")\n\n# model specific parameters\ninputs= []\n```\n\n\n# 00006_WGANGP_MMG_MASS_ROI\n\nWGAN-GP Model for Mammogram MASS Patch Generation (Trained on BCDR)  \\\n<sub> **Note:** A wasserstein generative adversarial network with gradient penalty (WGAN-GP) that generates mass patches of mammograms. Pixel dimensions are 128x128. The DCGAN was trained on MMG patches from the BCDR dataset (Lopez et al, 2012). The uploaded ZIP file contains the files 10000.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, and the GAN model architecture (in pytorch) below the /src folder. </sub>\n\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Mass                  |   mammography   |    wgan-gp     |  128x128  |     [BCDR](https://bcdr.eu/information/about)      | ![sample](_static/samples/00006.png) |  `00006_WGANGP_MMG_MASS_ROI`  | [Zenodo (6554713)](https://doi.org/10.5281/zenodo.6554713)  | [Szafranowska et al (2022)](https://doi.org/10.48550/arXiv.2203.04961)  | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(model_id=\"00006_WGANGP_MMG_MASS_ROI\")\n\n# model specific parameters\ninputs= []\n```\n\n\n# 00007_INPAINT_BRAIN_MRI\n\nTumor Inpainting Model for Generation of Flair, T1, T1c, T2 Brain MRI Images (Trained on BRATS) \\\n<sub> **Note:** A Generative adversarial network (GAN) for Inpainting tumors (based on concentric circle-based tumor grade masks) into multi-modal MRI images (Flair, T1, T1c, T2) with dimensions 256x256. Model was trained on BRATS MRI Dataset (Menze et al). For more information, see publication (https://doi.org/10.1002/mp.14701). Model comes with example input image folders. Apart from that, the uploaded ZIP file contains the model checkpoint files .pth (model weight), __init__.py (image generation method and utils), a requirements.txt, the MEDIGAN metadata.json. The proposed method synthesizes brain tumor images from normal brain images and concentric circles that are simplified tumor masks. The tumor masks are defined by complex features, such as grade, appearance, size, and location. Thus, these features of the tumor masks are condensed and simplified to concentric circles. In the proposed method, the user-defined concentric circles are converted to various tumor masks through deep neural networks. The normal brain images are masked by the tumor mask, and the masked region is inpainted with the tumor images synthesized by the deep neural networks. Also see original repository at: https://github.com/KSH0660/BrainTumor\" </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Brain Tumors on Flair, T1, T1c, T2 with Masks   |   brain MRI   |    inpaint GAN     |  256x256  |     [BRATS 2018](https://wiki.cancerimagingarchive.net/pages/viewpage.action?pageId=37224922)      | ![sample](_static/samples/00007_F.png) <br> ![sample](_static/samples/00007_T1.png) <br> ![sample](_static/samples/00007_T1c.png) <br> ![sample](_static/samples/00007_T2.png) <br> ![sample](_static/samples/00007_mask.png) <br> ![sample](_static/samples/00007_grade_mask.png) |  `00007_INPAINT_BRAIN_MRI`  |  [Zenodo (7041737)](https://doi.org/10.5281/zenodo.7041737)  | [Kim et al (2020)](https://doi.org/10.1002/mp.14701)  | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00007_INPAINT_BRAIN_MRI\",\n    image_size=256,\n    num_inpaints_per_sample=2,\n    randomize_input_image_order=True,\n    F_img_path=None,\n    T1_img_path=None,\n    T1c_img_path=None,\n    T2_img_path=None,\n    add_variations_to_mask=True,\n    x_center=130,\n    y_center=130,\n    radius_1=10,\n    radius_2=15,\n    radius_3=30,\n)\n\n# model specific parameters\ninputs= [\n    \"image_size: default=256, help=the size if height and width of the generated images.\",\n    \"num_inpaints_per_sample: default=2, help=the number of tumor inpaint images per MRI modality that is generated from the same input sample\",\n    \"randomize_input_image_order: default=True, help=input image order is randomized. This helps to not exclude input images if batch generation is used.\",\n    \"F_img_path: default=None, help=The path to the folder were the input Flair MRI images are stored.\",\n    \"T1_img_path: default=None, help=The path to the folder were the input T1 MRI images are stored.\",\n    \"T1c_img_path: default=None, help=The path to the folder were the input T1c MRI images are stored.\",\n    \"T2_img_path: default=None, help=The path to the folder were the input T2 MRI images are stored.\",\n    \"add_variations_to_mask: default=True, help=This slightly varies the values of x_center, y_center, radius_1, radius_2, radius_3. If True, the same segmentation masks is still used to generate each of the 4 modality images. This is recommended as it results in higher image diversity.\",\n    \"x_center: default=130, help=the x coordinate of the concentric circle upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\",\n    \"y_center: default=130, help=the y coordinate of the concentric circle upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\",\n    \"radius_1: default=10, help=the radius of the first (inside second) of three concentric circles (necrotic and non-enhancing tumor) upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\",\n    \"radius_2: default=15, help=the radius of the second (inside third) of three concentric circles (enhancing tumor) upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\",\n    \"radius_3: default=30, help=the radius of the third of three concentric circles (edema) upon which the binary mask, the tumor grade mask, and, ultimately, the generated images are based.\"\n]\n```\n\n\n# 00008_C-DCGAN_MMG_MASSES\n\nConditional DCGAN Model for Patch Generation of Mammogram Masses Conditioned on Biopsy Proven Malignancy Status (Trained on CBIS-DDSM)  \\\n<sub> **Note:** A class-conditional deep convolutional generative adversarial network that generates mass patches of mammograms that are conditioned to either be benign (1) or malignant (0). Pixel dimensions are 128x128. The Cond-DCGAN was trained on MMG patches from the CBIS-DDSM (Sawyer Lee et al, 2016). The uploaded ZIP file contains the files 1750.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the used GAN training config file, a test.sh file to run the model, and two folders with a few generated images. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Mass (Mal/Benign)     |   mammography   |    c-dcgan      |  128x128  |     [CBIS-DDSM](https://wiki.cancerimagingarchive.net/display/Public/CBIS-DDSM)      | ![sample](_static/samples/00008.png) |  `00008_C-DCGAN_MMG_MASSES`  | [Zenodo (6647349)](https://doi.org/10.5281/zenodo.6647349)  | |  \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00008_C-DCGAN_MMG_MASSES\",\n    condition=None,\n    z=None,\n    is_cbisddsm_training_data=False, # False generates with weights of GAN trained on cbis-ddsm testset\n)\n\n# model specific parameters\ninputs= [\n    \"condition: default=None, help=Either 0, 1 or None. Condition indicates whether a generated mass is malignant (0) or benign (1). If None, a balanced set of malignant and benign tumor images is created.\",\n    \"z: default=None, help=the input noise torch tensor for the generator. If None, this option is ignored (e.g. random input vector generation)\"\n    \"is_cbisddsm_training_data: default=True, help=Boolean indicating whether a GAN checkpoint trained on the predefined test or train dataset (predefined by cbis-ddsm dataset creators) should be used.\"\n]\n```\n\n\n# 00009_PGGAN_POLYP_PATCHES_W_MASKS\n\nPGGAN Model for Patch Generation of Polyps with Corresponding Segmentation Masks (Trained on HyperKvasir) \\\n<sub> **Note:** A Progressively-growing generative adversarial network that generates a 4 dimensional output containing an RGB image (channels 1-3) and a segmentation mask (channel 4). The RGB images are images of polyps and the segmentation mask indicates the location and shape of the polyp on the image. Pixel dimensions are 256x256. The model was trained on gastrointestinal endoscopy imaging data from the HyperKvasir dataset by Borgli et al (2020, 'https://doi.org/10.1038/s41597-020-00622-y'). The uploaded ZIP file contains the files ProGAN_300000_g.model (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the source code from the official repository ('https://github.com/vlbthambawita/singan-seg-polyp'), and a test.sh file to run the model, and a folder 'examples/' with a few generated images. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Polyp with Mask              |   endoscopy   |    pggan    |  256x256  |     [HyperKvasir](https://osf.io/mh9sj/)      | ![sample](_static/samples/00009.png) <br> ![sample](_static/samples/00009_mask.png) |  `00009_PGGAN_POLYP_PATCHES_W_MASKS`  | [Zenodo (6653743)](https://doi.org/10.5281/zenodo.6653743)  | [Thambawita et al (2022)](https://doi.org/10.1371/journal.pone.0267976)  | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00009_PGGAN_POLYP_PATCHES_W_MASKS\",\n    gpu_id=None, \n    channel=128, \n    z_dim=128, \n    pixel_norm=False, \n    img_channels=4, \n    tanh=False, \n    step=6, \n    alpha=1, \n    save_option=\"image_only\", \n    num_fakes=1000,\n)\n\n# model specific parameters\ninputs=[\n    \"gpu_id: type=int, default=None, help=0 is the first gpu, 1 is the second gpu, etc.\",\n    \"channel: type=int, default=128, help=determines how big the model is, smaller value means faster training, but less capacity of the model\",\n    \"z_dim: type=int, default=128, help=the initial latent vectors dimension, can be smaller such as 64, if the dataset is not diverse\",\n    \"pixel_norm: default=False, action=store_true, help=a normalization method inside the model, you can try use it or not depends on the dataset\",\n    \"img_channels: default=4, help=Number of channels in input data., for rgb images=3, gray=1 etc.\",\n    \"tanh: default=False, action=store_true, help=an output non-linearity on the output of Generator, you can try use it or not depends on the dataset\",\n    \"step: default=6, help=step to generate fake data. # can be 1 = 8, 2 = 16, 3 = 32, 4 = 64, 5 = 128, 6 = 256\",\n    \"alpha: default=1, help=Progressive gan parameter to set, 0 or 1\",\n    \"save_option: default=image_only, help=Options to save output, image_only, mask_only, image_and_mask, choices=[image_only,mask_only, image_and_mask]\",\n    \"num_fakes: default=1000, help=Number of fakes to generate, type=int\"\n]\n```\n\n\n# 00010_FASTGAN_POLYP_PATCHES_W_MASKS\n\nFastGAN Model for Patch Generation of Polyps with Corresponding Segmentation Masks (Trained on HyperKvasir) \\\n<sub> **Note:** A Fast generative adversarial network (FastGAN) that generates a 4 dimensional output containing an RGB image (channels 1-3) and a segmentation mask (channel 4). FASTGAN is from the paper 'Towards Faster and Stabilized GAN Training for High-fidelity Few-shot Image Synthesis' in ICLR 2021. The RGB images are images of polyps and the segmentation mask indicates the location and shape of the polyp on the image. Pixel dimensions are 256x256. The model was trained on gastrointestinal endoscopy imaging data from the HyperKvasir dataset by Borgli et al (2020, 'https://doi.org/10.1038/s41597-020-00622-y'). The uploaded ZIP file contains the files FastGAN_all_50000.pth (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the source code from the repository ('https://github.com/vlbthambawita/singan-seg-polyp'), and a test.sh file to run the model, and a folder 'examples/' with a few generated images. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Polyp with Mask              |   endoscopy   |    fastgan  |  256x256  |     [HyperKvasir](https://osf.io/mh9sj/)      | ![sample](_static/samples/00010.png) <br> ![sample](_static/samples/00010_mask.png) |  `00010_FASTGAN_POLYP_PATCHES_W_MASKS`  | [Zenodo (6660711)](https://doi.org/10.5281/zenodo.6660711)  | [Thambawita et al (2022)](https://doi.org/10.1371/journal.pone.0267976)  | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00010_FASTGAN_POLYP_PATCHES_W_MASKS\",\n    gpu_id=None, \n    save_option=\"image_and_mask\",\n)\n\n# model specific parameters\ninputs=[\n    \"gpu_id: type=int, default=None, help=0 is the first gpu, 1 is the second gpu, etc.\",\n    \"save_option: default=image_only, help=Options to save output, image_only, mask_only, image_and_mask, choices=[image_only,mask_only, image_and_mask]\"\n]\n```\n\n\n# 00011_SINGAN_POLYP_PATCHES_W_MASKS\n\nSinGAN Model for Patch Generation of Polyps with Corresponding Segmentation Masks (Trained on HyperKvasir) \\\n<sub> **Note:** A SinGAN generative adversarial network that generates a 2dimensional output image tuple containing a 3-channel RGB image and a 3-channel segmentation mask. SinGAN is from the paper 'SinGAN: Learning a Generative Model from a Single Natural Image' in ICCV 2019. The width of the outputted images varies depending on the corresponding original image. The RGB images are images of polyps and the segmentation mask indicates the location and shape of the polyp on the image. Pixel dimensions are 213x256. The model was trained on gastrointestinal endoscopy imaging data from the HyperKvasir dataset by Borgli et al (2020, 'https://doi.org/10.1038/s41597-020-00622-y'). The uploaded ZIP file contains the checkpoints in folder 'SinGAN-Generated' (model weights), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the source code from the repository ('https://github.com/vlbthambawita/singan-seg-polyp'), and a test.sh file to run the model, and a folder 'examples/' with a few generated images. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Polyp with Mask              |   endoscopy   |    singan  |  ≈250x250  |     [HyperKvasir](https://osf.io/mh9sj/)      | ![sample](_static/samples/00011.png) <br> ![sample](_static/samples/00011_mask.png) |  `00011_SINGAN_POLYP_PATCHES_W_MASKS`  | [Zenodo (6667944)](https://doi.org/10.5281/zenodo.6667944)  | [Thambawita et al (2022)](https://doi.org/10.1371/journal.pone.0267976)  | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00011_SINGAN_POLYP_PATCHES_W_MASKS\",\n    model_files=\"models/00011_SINGAN_POLYP_PATCHES_W_MASKS/singan_seg_polyp/SinGAN-Generated\",\n    gen_start_scale=0,\n    checkpoint_ids=None,\n    multiple_checkpoints=False\n)\n\n# model specific parameters\ninputs= [\n    \"model_files: default=models/00011_SINGAN_POLYP_PATCHES_W_MASKS/singan_seg_polyp/SinGAN-Generated help=the folder where the checkpoints are stored | \",\n    \"gen_start_scale: default=0 help=The start for scaling (progressively increasing generator input size) in SinGAN.\",\n    \"checkpoint_ids: default=None help=A list of checkpoint ids that will be used for polyp generation. If None, all available checkpoints (i.e. 1000) or one random one (depending on 'multiple_checkpoints' arg) will be used.\",\n    \"multiple_checkpoints: default=False help=A boolean indicating if all checkpoint_ids or one random one is used for generating images, but only in case 'checkpoint_ids'==None\"\n]\n```\n\n\n# 00012_C-DCGAN_MMG_MASSES\n\nConditional DCGAN Model for Patch Generation of Mammogram Masses Conditioned on Biopsy Proven Malignancy Status (Trained on BCDR) \\\n<sub> **Note:** A class-conditional deep convolutional generative adversarial network that generates mass patches of mammograms that are conditioned to either be benign (1) or malignant (0). Pixel dimensions are 128x128. The Cond-DCGAN was trained on MMG patches from the BCDR dataset (Lopez et al, 2012). The uploaded ZIP file contains the files 1250.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata, the used GAN training config file, a test.sh file to run the model, and two folders with a few generated images. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Mass (Mal/Benign)     |   mammography   |    c-dcgan      |  128x128  |     [BCDR](https://bcdr.eu/information/about)      | ![sample](_static/samples/00012.png) |  `00012_C-DCGAN_MMG_MASSES`  | [Zenodo (6755693)](https://doi.org/10.5281/zenodo.6818095)  | | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00012_C-DCGAN_MMG_MASSES\",\n    condition=None,\n    z=None,\n)\n\n# model specific parameters\ninputs= [\n    \"condition: default=None, help=Either 0, 1 or None. Condition indicates whether a generated mass is malignant (0) or benign (1). If None, a balanced set of malignant and benign tumor images is created.\",\n    \"z: default=None, help=the input noise torch tensor for the generator. If None, this option is ignored (e.g. random input vector generation)\"\n]\n```\n\n\n# 00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO\n\nCycleGAN Model for Low-to-High Brest Density Mammograms Translation of MLO VIEW (Trained on OPTIMAM) \\\n<sub> **Note:** A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from OPTIMAM dataset (Halling-Brown et al, 2014). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Density Transfer MLO  |   mammography   |    cyclegan    | 1332x800  |     [OPTIMAM](https://doi.org/10.48550/arXiv.2004.04742)      | ![sample](_static/samples/00013.png) |  `00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO`  | [Zenodo (6818095)](https://doi.org/10.5281/zenodo.6818095)  | [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) |\n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO\",\n    input_path=\"models/00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO/images\", \n    image_size=[1332, 800], \n    gpu_id=0, \n    translate_all_images=False, \n    low_to_high=True,\n)\n\n# model specific parameters\ninputs= [\n    \"input_path: default=models/00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n    \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n    \"gpu_id: default=0, help=the gpu to run the model on.\",\n    \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n    \"low_to_high: default=True, help=if true, breast density is added. If false, it is removed from the input image. A different generator of the cycleGAN is used based no this flag.\"\n]\n```\n\n\n# 00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC\n\nCycleGAN Model for Low-to-High Brest Density Mammograms Translation of CC VIEW (Trained on OPTIMAM) \\\n<sub> **Note:** A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from OPTIMAM dataset (Halling-Brown et al, 2014). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder. </sub>\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Density Transfer CC   |   mammography   |    cyclegan    | 1332x800  |     [OPTIMAM](https://doi.org/10.48550/arXiv.2004.04742)      | ![sample](_static/samples/00014.png) |  `00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC`  | [Zenodo (6818103)](https://doi.org/10.5281/zenodo.6818103)  | [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) |\n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC\",\n    input_path=\"models/00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC/images\", \n    image_size=[1332, 800], \n    gpu_id=0, \n    translate_all_images=False, \n    low_to_high=True,\n)\n\n# model specific parameters\ninputs= [\n    \"input_path: default=models/00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n    \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n    \"gpu_id: default=0, help=the gpu to run the model on.\",\n    \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n    \"low_to_high: default=True, help=if true, breast density is added. If false, it is removed from the input image. A different generator of the cycleGAN is used based no this flag.\"\n]\n```\n\n\n# 00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO\n\nCycleGAN Model for Low-to-High Brest Density Mammograms Translation of MLO VIEW (Trained on CSAW) \\\n<sub> **Note:** A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from CSAW dataset (Dembrower et al., 2020, https://doi.org/10.1007/s10278-019-00278-0). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder. </sub>\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Density Transfer MLO  |   mammography   |    cyclegan    | 1332x800  |     [CSAW](https://link.springer.com/article/10.1007/s10278-019-00278-0)      | ![sample](_static/samples/00015.png) |  `00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO`  | [Zenodo (6818105)](https://doi.org/10.5281/zenodo.6818105)  | [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) |\n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO\",\n    input_path=\"models/00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO/images\", \n    image_size=[1332, 800], \n    gpu_id=0, \n    translate_all_images=False, \n    low_to_high=True,\n)\n\n# model specific parameters\ninputs= [\n    \"input_path: default=models/00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n    \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n    \"gpu_id: default=0, help=the gpu to run the model on.\",\n    \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n    \"low_to_high: default=True, help=if true, breast density is added. If false, it is removed from the input image. A different generator of the cycleGAN is used based no this flag.\"\n]\n```\n\n\n# 00016_CYCLEGAN_MMG_DENSITY_CSAW_CC\n\nCycleGAN Model for Low-to-High Brest Density Mammograms Translation of CC VIEW (Trained on CSAW) \\\n<sub> **Note:** A cycle generative adversarial network (CycleGAN) that generates mammograms with high breast density from an original mammogram e.g. with low-breast density. The CycleGAN was trained using normal (without pathologies) digital mammograms from CSAW dataset (Dembrower et al., 2020, https://doi.org/10.1007/s10278-019-00278-0). The uploaded ZIP file contains the files CycleGAN_high_density.pth (model weights), __init__.py (image generation method and utils) and the GAN model architecture (in pytorch) below the /src folder. </sub>\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Breast Density Transfer CC   |   mammography   |    cyclegan    | 1332x800  |     [CSAW](https://link.springer.com/article/10.1007/s10278-019-00278-0)     | ![sample](_static/samples/00016.png) |  `00016_CYCLEGAN_MMG_DENSITY_CSAW_CC`  | [Zenodo (6818107)](https://doi.org/10.5281/zenodo.6818107)  | [Garrucho et al (2022)](https://doi.org/10.48550/arXiv.2209.09809) |\n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00016_CYCLEGAN_MMG_DENSITY_CSAW_CC\",\n    input_path=\"models/00016_CYCLEGAN_MMG_DENSITY_CSAW_CC/images\", \n    image_size=[1332, 800], \n    gpu_id=0, \n    translate_all_images=False, \n    low_to_high=True,\n)\n\n# model specific parameters\ninputs= [\n    \"input_path: default=models/00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO/images, help=the path to .png mammogram images that are translated from low to high breast density or vice versa\",\n    \"image_size: default=[1332, 800], help=list with image height and width. Images are rescaled to these pixel dimensions.\",\n    \"gpu_id: default=0, help=the gpu to run the model on.\",\n    \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n    \"low_to_high: default=True, help=if true, breast density is added. If false, it is removed from the input image. A different generator of the cycleGAN is used based no this flag.\"\n]\n```\n\n\n\n# 00017_DCGAN_XRAY_LUNG_NODULES\n\nDCGAN Model for Patch Generation of Lung Nodules (Trained on Node21) \\\n<sub> **Note:** An unconditional deep convolutional generative adversarial network (DCGAN) that generates lung nodule regions-of-interest patches based on chest xray (CXR) images. The pixel dimension of the generated patches is 128x128. The WGANGP was trained on cropped patches from CXR images from the NODE21 dataset (Sogancioglu et al, 2021). The uploaded ZIP file contains the files model.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata.json file, the used GAN training config file, a test.sh file to run the model, and an /image folder with a few generated example images. </sub>\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Lung Nodules                 |   chest x-ray   |    dcgan       | 128x128   |     [NODE21](https://zenodo.org/record/4725881#.YxNmNuxBwXA)      | ![sample](_static/samples/00017.png) |  `00017_DCGAN_XRAY_LUNG_NODULES`  | [Zenodo (6943691)](https://doi.org/10.5281/zenodo.6943691)  | | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(model_id=\"00017_DCGAN_XRAY_LUNG_NODULES\")\n\n# model specific parameters\ninputs= []\n```\n\n\n\n\n# 00018_WGANGP_XRAY_LUNG_NODULES\n\nWGANGP Model for Patch Generation of Lung Nodules (Trained on Node21) \\\n<sub> **Note:** An unconditional wasserstein generative adversarial network with gradient penalty (WGAN_GP) that generates lung nodule regions-of-interest patches based on chest xray (CXR) images. The pixel dimension of the generated patches is 128x128. The WGANGP was trained on cropped patches from CXR images from the NODE21 dataset (Sogancioglu et al, 2021). The uploaded ZIP file contains the files model.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata.json file, the used GAN training config file, a test.sh file to run the model, and an /image folder with a few generated example images. </sub>\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Lung Nodules                 |   chest x-ray   |    wgan-gp       | 128x128   |     [NODE21](https://zenodo.org/record/4725881#.YxNmNuxBwXA)      | ![sample](_static/samples/00018.png) |  `00018_WGANGP_XRAY_LUNG_NODULES`  | [Zenodo (6943761)](https://doi.org/10.5281/zenodo.6943761)  | | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(model_id=\"00018_WGANGP_XRAY_LUNG_NODULES\")\n\n# model specific parameters\ninputs= []\n```\n\n\n\n# 00019_PGGAN_CHEST_XRAY\n\nPGGAN Model for Generation of Chest XRAY (CXR) Images (Trained on ChestX-ray14 Dataset) \\\n<sub> **Note:** An unconditional Progressively-growing generative adversarial network (PGGAN) that generates chest xray (CXR) images with pixel dimensions 1024x1024. The PGGAN was trained on CXR images from the ChestX-ray14 Dataset (Wang et al., 2017, Paper: https://arxiv.org/pdf/1705.02315.pdf, Data: https://nihcc.app.box.com/v/ChestXray-NIHCC). The uploaded ZIP file contains the files model.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, a LICENSE file, the MEDIGAN metadata.json file, the used GAN training config file, a test.sh file to run the model, and an /image folder with a few generated example images. </sub>\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Chest Xray Images            |   chest x-ray   |    pggan       | 1024x1024   |     [ChestX-ray14](https://nihcc.app.box.com/v/ChestXray-NIHCC/folder/36938765345)      | ![sample](_static/samples/00019.png) |  `00019_PGGAN_CHEST_XRAY`  | [Zenodo (6943803)](https://doi.org/10.5281/zenodo.6943803)  | | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(model_id=\"00019_PGGAN_CHEST_XRAY\")\n\n# model specific parameters\ninputs= []\n```\n\n\n\n# 00020_PGGAN_CHEST_XRAY\n\nPGGAN Model for Generation of Chest XRAY (CXR) Images (Trained on ChestX-ray14 Dataset) \\\n<sub> **Note:** An unconditional Progressively-growing generative adversarial network (PGGAN) that generates chest xray (CXR) images with pixel dimensions 1024x1024. The PGGAN was trained on CXR images from the ChestX-ray14 Dataset (Wang et al., 2017, Paper: https://arxiv.org/pdf/1705.02315.pdf, Data: https://nihcc.app.box.com/v/ChestXray-NIHCC). The uploaded ZIP file contains the model weights checkpoint file, __init__.py (image generation method and utils), a requirements.txt, the MEDIGAN metadata.json file, a test.sh file to run the model, and an /image folder with a few generated example images. </sub>\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Chest Xray Images            |   chest x-ray   |    pggan       | 1024x1024   |     [ChestX-ray14](https://nihcc.app.box.com/v/ChestXray-NIHCC/folder/36938765345)      | ![sample](_static/samples/00020.png) |  `00020_PGGAN_CHEST_XRAY`  | [Zenodo (7046280)](https://doi.org/10.5281/zenodo.7046280)  |  [Segal et al (2021)](https://doi.org/10.1007/s42979-021-00720-7)  |\n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00020_PGGAN_CHEST_XRAY\",\n    image_size=1024,\n    resize_pixel_dim=None,\n)\n\n# model specific parameters\ninputs = [\n    \"image_size: default=1024, help=the size if height and width of the generated images\",\n    \"resize_pixel_dim: default=None, help=Resizing of generated images via the pillow PIL image library.\"\n]\n```\n\n\n# 00021_CYCLEGAN_BRAIN_MRI_T1_T2\n\nCycleGAN Brain MRI T1-T2 translation (trained on CrossMoDA 2021 dataset)\n\n<sub> **Note:** In recent years, deep learning models have considerably advanced the performance of segmentation tasks on Brain Magnetic Resonance Imaging (MRI). However, these models show a considerable performance drop when they are evaluated on unseen data from a different distribution. Since annotation is often a hard and costly task requiring expert supervision, it is necessary to develop ways in which existing models can be adapted to the unseen domains without any additional labelled information. In this work, we explore one such technique which extends the CycleGAN [2] architecture to generate label-preserving data in the target domain. The synthetic target domain data is used to train the nn-UNet [3] framework for the task of multi-label segmentation. The experiments are conducted and evaluated on the dataset [1] provided in the ‘Cross-Modality Domain Adaptation for Medical Image Segmentation’ challenge [23] for segmentation of vestibular schwannoma (VS) tumour and cochlea on contrast enhanced (ceT1) and high resolution (hrT2) MRI scans. In the proposed approach, our model obtains dice scores (DSC) 0.73 and 0.49 for tumour and cochlea respectively on the validation set of the dataset. This indicates the applicability of the proposed technique to real-world problems where data may be obtained by different acquisition protocols as in [1] where hrT2 images are more reliable, safer, and lower-cost alternative to ceT1. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n| Brain T1-T2 MRI Modality Transfer |  brain MRI  |   cyclegan      | 224x192  |    [CrossMoDA 2021](https://arxiv.org/abs/2201.02831)     | ![sample](_static/samples/00021.png) | `00021_CYCLEGAN_BRAIN_MRI_T1_T2` | [Zenodo (7074555)](https://doi.org/10.5281/zenodo.7074555) | [Joshi et al (2022)](https://doi.org/10.1007/978-3-031-09002-8_47) |\n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00021_CYCLEGAN_BRAIN_MRI_T1_T2\",\n    input_path= \"models/00021_CYCLEGAN_BRAIN_MRI_T1_T2/inputs/T1\", # or /T2\n    image_size=[224, 192],\n    gpu_id=0,\n    translate_all_images=True,\n    T1_to_T2=True,\n)\n\n# model specific parameters\ninputs = [\n    \"input_path: default=models/00021_CYCLEGAN_BRAIN_MRI_T1_T2/inputs/T1, help=the path to .png brain MRI images that are translated from T1 to T2 or vice versa. \",\n    \"image_size: default=[224, 192], help=list with image height and width. \",\n    \"gpu_id: default=0, help=the gpu to run the model on.\",\n    \"translate_all_images: default=False, help=flag to override num_samples in case the user wishes to translate all images in the specified input_path folder.\",\n    \"T1_to_T2: default=True, help=if true, generator for T1 to T2 translation is used. If false, the translation is done from T2 to T1 instead. Need to adjust input path in this case e.g. models/00021_CYCLEGAN_BRAIN_MRI_T1_T2/inputs/T2 instead of models/00021_CYCLEGAN_BRAIN_MRI_T1_T2/inputs/T1. A different generator of the cycleGAN is used based on this flag.\"\n]\n```\n\n# 00022_WGAN_CARDIAC_AGING\n\nConditional WGANGP Model for Cardiac Image generation with age offset (Trained on UK Biobank) \\\n<sub> **Note:** A conditional wasserstein generative adversarial network with gradient penalty (WGAN_GP) that generates MRI cardiac images. The pixel dimension of the generated images is 256x256. The uploaded ZIP file contains the files model.ckpt (model weights), __init__.py (image generation method and utils), a requirements.txt, and the used GAN training config file. A sample_image.png is provided for example generation. </sub>\n\n\n| Output type                     |  Modality   |      Model type     |   Output size    |  Base dataset   |     Output examples       |    `model_id`      |  Hosted on   |  Reference  |\n|-----------------------------|:--------:|:-------------:|:--------:|:------------:|:------:|:------:|:------:|:------:|\n|  Cardiac image              |   MRI   |    wgan  |  256x256  |     [UK Biobank](https://www.ukbiobank.ac.uk/)      | ![sample](_static/samples/00022.png) |  `00022_WGAN_CARDIAC_AGING`  | [Zenodo (7494368)](https://doi.org/10.5281/zenodo.7494368)  | [Campello et al (2022)](https://doi.org/10.3389/fcvm.2022.983091)  | \n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00022_WGAN_CARDIAC_AGING\",\n    image_paths_input=[\n                            \"models/00022_WGAN_CARDIAC_AGING/sample_image.png\",\n                            \"models/00022_WGAN_CARDIAC_AGING/sample_image.png\",\n                            \"models/00022_WGAN_CARDIAC_AGING/sample_image.png\"\n                        ],\n    aging_input=[\n                    -4,\n                    10,\n                    2\n                ],\n    data_type=\"2d\",\n    view=\"la\",\n    subcat=\"2ch\",\n)\n\n# model specific parameters\ninputs= [\n    \"input_image_paths: default=[\\\"models/00022_WGAN_CARDIAC_AGING/sample_image.png\\\"] help=List of image paths to apply aging. One channel .png images must be provided.\",\n    \"aging_input: default=[-4] help=List of age offset values for each image.\",\n    \"data_type: default=\\\"2d\\\" help=\",\n    \"view: default=\\\"la\\\" help=\",\n    \"subcat: default=\\\"2ch\\\", help=\"\n]\n```\n\n# 00023_PIX2PIXHD_BREAST_DCEMRI\n\nPix2Pix model for DCE-MRI slice generation from pre-contrast image input (Trained on Duke Breast MRI Dataset) \\\n<sub> **Note:** A pix2pixHD mmodel that generates DCE-MRI axial slices based on checkpoint after 30 training epochs. \nThe pixel dimension of the generated images is 512x512. Several generated 2d slices can be merged together to create a 3D MRI volume with tumour tissue highlighted by synthetic contrast. \nThe uploaded ZIP file contains the files 30_net_G.pth (model weights), __init__.py (image generation method and utils), a requirements.txt, and further code below the /src folder for handling of model, data, and training utils. Sample input images are provided as an example for image generation. </sub>\n\n\n| Output type                    | Modality | Model type | Output size |  Base dataset   |           Output examples            |    `model_id`      |  Hosted on   |  Reference  |\n|--------------------------------|:--------:|:----------:|:-----------:|:------------:|:------------------------------------:|:------:|:------:|:------:|\n| DCE-MRI sequence 1 axial slice | DCE-MRI  | pix2pixHD  |   512x512   |     [Duke Dataset](https://sites.duke.edu/mazurowski/resources/breast-cancer-mri-dataset/)      | ![sample](_static/samples/00023.png) |  `00023_PIX2PIXHD_BREAST_DCEMRI`  | [Zenodo (10210944)](https://zenodo.org/doi/10.5281/zenodo.10210944)  | [Osuala et al (2023)](https://doi.org/10.48550/arXiv.2311.10879)  | \n\n\n```python\n# create samples with this model\nfrom medigan import Generators\nGenerators().generate(\n    model_id=\"00023_PIX2PIXHD_BREAST_DCEMRI\",\n    input_path= \"input/\",\n    image_size=[512, 512],\n    gpu_id=0,\n)\n\n# model specific parameters\ninputs = [\n    \"input_path: default=input/, help=the path to .png breast DCE-MRI images that are translated from pre-contrast to the first DCE post-contrast sequence. \",\n    \"image_size: default=[512, 512], help=list with image height and width. \",\n    \"gpu_id: default=0, help=the gpu to run the model on.\",\n] \n```"
  },
  {
    "path": "docs/source/models.rst",
    "content": "Generative Models\n=======================\n\nThis section provides an overview of the generative models in `medigan`.\n\nFind in the tables below for each model:\n\n#. A **model_id**\n#. A link to detailed documentation on **Zenodo**\n\nFurther model information can be found in the `global.json <https://github.com/RichardObi/medigan/blob/main/config/global.json>`_ metadata.\n\n.. warning::\n    Some of the model internal checkpoint loading functions may implicitly use the pickle module (e.g. `torch.load() <https://pytorch.org/docs/stable/generated/torch.load.html>`_),\n    Pickle is insecure: It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (`example video <https://youtu.be/2ethDz9KnLk>`_).\n    While we do our best to analyse and test each model before Zenodo upload and `medigan` integration, we cannot provide a security guarantee. Be aware and run only models you trust.\n    To further mitigate risks, we plan to integrate a malware scanning tool into medigan's `CI pipeline <https://github.com/RichardObi/medigan/actions>`_.\n\n\n.. include:: model_documentation.md\n   :parser: myst_parser.sphinx_\n"
  },
  {
    "path": "docs/source/modules_overview.rst",
    "content": "Modules Overview\n=============\n\n.. contents:: Table of Contents\n   :depth: 2\n\nGenerators\n__________________________\n.. automodapi:: medigan.generators\n    :no-inheritance-diagram:\n|\n\nModelExecutor\n__________________________\n.. automodapi:: medigan.execute_model.model_executor\n    :no-inheritance-diagram:\n|\n\nModelVisualizer\n__________________________\n.. automodapi:: medigan.model_visualizer\n    :no-inheritance-diagram:\n|\n\nModelSelector\n__________________________\n.. automodapi:: medigan.select_model.model_selector\n    :no-inheritance-diagram:\n|\n\nModelContributor\n__________________________\n.. automodapi:: medigan.contribute_model.model_contributor\n    :no-inheritance-diagram:\n|\n\nConfigManager\n__________________________\n.. automodapi:: medigan.config_manager\n    :no-inheritance-diagram:\n|"
  },
  {
    "path": "docs/source/tests.rst",
    "content": "Tests\n==============\n\n.. contents:: Table of Contents\n\nAutomated continuous integration (CI) tests (`GitHub actions <https://github.com/RichardObi/medigan/actions>`_) are triggered by commits to the medigan repository.\nThese CI tests can be found `here <https://github.com/RichardObi/medigan/tree/main/tests>`_.\n\nApart from that, to facilitate testing if `medigan` is setup correctly and whether all of the features in `medigan` work as desired, the following set of automated test cases is provided.\nBelow, each test function is described and a command is provided to run each test.\n\nSetup medigan for running tests\n_______________________________________\nOpen your command line, and clone `medigan` from Github with:\n\n.. code-block:: Python\n\n    git clone https://github.com/RichardObi/medigan.git\n    cd medigan\n\nTo install dependencies and to setup and activate a virtual environment, run:\n\n.. code-block:: Python\n\n    pip install pipenv\n    pipenv install\n    pipenv shell\n\n\nTest 1: test_medigan_imports\n_______________________________________\nThis test checks if `medigan` can be imported correctly.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_medigan_imports\n\nTest 2: test_init_generators\n_______________________________________\nThis test checks if the central `generators` class can be initialised correctly.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_init_generators\n\n\nTest 3: test_generate_methods\n_______________________________________\nThis test examines whether samples can be created with any of three example generative models (`1 <https://doi.org/10.5281/zenodo.5187714>`_, `2 <https://doi.org/10.5281/zenodo.5188557>`_, `3 <https://doi.org/10.5281/zenodo.5547263>`_) in `medigan`.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_generate_methods\n\n\nTest 4: test_generate_methods_with_additional_args\n______________________________________________________\nAdditional key-value pair arguments (kwargs) can be provided to the `generate()` method of a generative model.\nThis test checks if these additional arguments are passed correctly to the generate method and whether the generate method's returned result corresponds to the passed arguments.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_generate_methods_with_additional_args\n\n\nTest 5: test_get_generate_method\n______________________________________________________\nThe `generate()` method of any of the generative models in `medigan` can be returned.\nThis makes it easier to integrate the `generate()` function dynamically into users' data processing and training pipelines i.e. avoiding it to reload the model weights each time it is called.\nThis test tests if the `generate()` method is successfully returned and usable thereafter.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_get_generate_method\n\n\nTest 6: test_search_for_models_method\n______________________________________________________\nThe tested function searches for a model by matching provided key words with the information in the model's `config <https://github.com/RichardObi/medigan-models>`_.\nThis test checks whether the expected models are found accordingly.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_search_for_models_method\n\n\nTest 7: test_find_model_and_generate_method\n______________________________________________________\nAfter searching and finding one specific model, the tested function generates samples with that model.\nThis test checks whether the expected model is found and whether samples are generated accordingly.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_find_model_and_generate_method\n\n\nTest 8: test_rank_models_by_performance\n______________________________________________________\nProvided a list of model ids, the tested function rankes these models by a performance metric.\nThe performance metrics are stored in the models' `config <https://github.com/RichardObi/medigan-models>`_.\nThis test checks whether the ranking worked and whether the expected model is ranked the highest.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_rank_models_by_performance\n\n\nTest 9: test_find_and_rank_models_by_performance\n______________________________________________________\nAfter searching and finding various models, the tested function ranks these models by a performance metric.\nThis test checks whether the expected model is found, and whether it is the highest ranked one and whether it generated samples accordingly.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_find_and_rank_models_by_performance\n\n\nTest 10: test_find_and_rank_models_then_generate_method\n_____________________________________________________________________\nAfter searching and finding various models, the tested function ranks these models by a performance metric and generates samples with the highest ranked model.\nThis test checks whether the expected model is found, whether it is the highest ranked one and whether it generated samples accordingly.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_find_and_rank_models_then_generate_method\n\n\nTest 11: test_get_models_by_key_value_pair\n______________________________________________________\nAfter receiving a key value pair, the tested function returns all models that have that key-value pair in their model `config <https://github.com/RichardObi/medigan-models>`_.\nThis test checks whether the expected models are found and returned correctly.\n\n.. code-block:: Python\n\n    python -m tests.tests TestMediganMethods.test_get_models_by_key_value_pair\n\n\n"
  },
  {
    "path": "examples/tutorial.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# MEDIGAN Quick start\\n\",\n    \"Quick introduction on how to choose the right model and generate images\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"pip install medigan\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Import medigan and initialize Generators\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from medigan import Generators\\n\",\n    \"generators = Generators()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Generate 10 samples using one of the medigan models\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"generators.generate(model_id=\\\"00001_DCGAN_MMG_CALC_ROI\\\", num_samples=10)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Get the model's generate method and run it to generate 3 samples\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"gen_function = generators.get_generate_function(model_id=\\\"00001_DCGAN_MMG_CALC_ROI\\\", \\n\",\n    \"                                                num_samples=3)\\n\",\n    \"gen_function()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Create a list of search terms and find the models that have these terms in their config.\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"values_list = ['dcgan', 'Mammography', 'inbreast']\\n\",\n    \"models = generators.find_matching_models_by_values(values=values_list, \\n\",\n    \"                                                    target_values_operator='AND', \\n\",\n    \"                                                    are_keys_also_matched=True, \\n\",\n    \"                                                    is_case_sensitive=False)\\n\",\n    \"print(f'Found models: {models}')\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Create a list of search terms, find a model and generate\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"values_list = ['dcgan', 'mMg', 'ClF', 'modalities', 'inbreast']\\n\",\n    \"generators.find_model_and_generate(values=values_list, \\n\",\n    \"                                    target_values_operator='AND', \\n\",\n    \"                                    are_keys_also_matched=True, \\n\",\n    \"                                    is_case_sensitive=False, \\n\",\n    \"                                    num_samples=5)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Rank the models by a performance metric and return ranked list of models\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"ranked_models = generators.rank_models_by_performance(metric=\\\"SSIM\\\", \\n\",\n    \"                                                        order=\\\"asc\\\")\\n\",\n    \"print(ranked_models)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Find the models, then rank them by a performance metric and return ranked list of models\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"ranked_models = generators.find_models_and_rank(values=values_list, \\n\",\n    \"                                                target_values_operator='AND',\\n\",\n    \"                                                are_keys_also_matched=True,\\n\",\n    \"                                                is_case_sensitive=False, \\n\",\n    \"                                                metric=\\\"SSIM\\\", \\n\",\n    \"                                                order=\\\"asc\\\")\\n\",\n    \"print(ranked_models)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Find the models, then rank them, and then generate samples with the best ranked model.\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"generators.find_models_rank_and_generate(values=values_list, \\n\",\n    \"                                        target_values_operator='AND',\\n\",\n    \"                                        are_keys_also_matched=True,\\n\",\n    \"                                        is_case_sensitive=False, \\n\",\n    \"                                        metric=\\\"SSIM\\\", \\n\",\n    \"                                        order=\\\"asc\\\", \\n\",\n    \"                                        num_samples=5)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Find all models that contain a specific key-value pair in their model config.\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"key = \\\"modality\\\"\\n\",\n    \"value = \\\"Full-Field Mammography\\\"\\n\",\n    \"found_models = generators.get_models_by_key_value_pair(key1=key, \\n\",\n    \"                                                        value1=value, \\n\",\n    \"                                                        is_case_sensitive=False)\\n\",\n    \"print(found_models)\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"interpreter\": {\n   \"hash\": \"aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49\"\n  },\n  \"kernelspec\": {\n   \"display_name\": \"Python 3.9.10 64-bit\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.8.12\"\n  },\n  \"orig_nbformat\": 4\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "models/__init__.py",
    "content": ""
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"setuptools\", \"wheel\"]\nbuild-backend = \"setuptools.build_meta:__legacy__\""
  },
  {
    "path": "setup.py",
    "content": "# coding: utf-8\nimport setuptools\n\nwith open(\"README.md\", \"r\") as fh:\n    long_description = fh.read()\n\nsetuptools.setup(\n    name=\"medigan\",\n    version=\"0.0.2\",\n    author=\"Richard Osuala, Grzegorz Skorupko, Noussair Lazrak\",\n    description=\"medigan is a modular open-source Python library that provides an interface to multiple generative models and automates synthetic dataset generation.\",\n    long_description=long_description,\n    long_description_content_type=\"text/markdown\",\n    url=\"https://github.com/RichardObi/medigan\",\n    project_urls={\n        \"Bug Tracker\": \"https://github.com/RichardObi/medigan/issues\",\n    },\n    classifiers=[\n        \"Programming Language :: Python :: 3\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Operating System :: OS Independent\",\n    ],\n    python_requires=\">=3.6\",\n    package_dir={\"\": \"src\"},\n    packages=setuptools.find_packages(where=\"src\"),\n    install_requires=[\"tqdm\", \"requests\", \"torch\", \"numpy\", \"PyGithub\", \"matplotlib\"],\n)\n"
  },
  {
    "path": "src/medigan/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" `medigan` is a modular Python library for automating synthetic dataset generation. \"\"\"\n# Set default logging handler to avoid \"No handler found\" warnings.\nimport logging\nfrom logging import NullHandler\n\n# importing the generators module and class for the convenience of extending the \"medigan.generators\" namespace to\n# \"medigan\", allowing 'from medigan import Generators'\nfrom .generators import Generators\n\nlogging.getLogger(__name__).addHandler(NullHandler())\n"
  },
  {
    "path": "src/medigan/config_manager.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" Config manager class that downloads, ingests, parses, and prepares the config information for all models. \"\"\"\n\n# Import python native libs\nfrom __future__ import absolute_import\n\nimport json\nimport logging\nfrom pathlib import Path\n\n# Import library internal modules\nfrom .constants import (\n    CONFIG_FILE_FOLDER,\n    CONFIG_FILE_KEY_DEPENDENCIES,\n    CONFIG_FILE_KEY_EXECUTION,\n    CONFIG_FILE_KEY_GENERATE,\n    CONFIG_FILE_KEY_GENERATE_ARGS,\n    CONFIG_FILE_KEY_GENERATE_ARGS_BASE,\n    CONFIG_FILE_KEY_GENERATE_ARGS_MODEL_FILE,\n    CONFIG_FILE_KEY_GENERATE_ARGS_NUM_SAMPLES,\n    CONFIG_FILE_KEY_GENERATE_ARGS_OUTPUT_PATH,\n    CONFIG_FILE_KEY_GENERATE_ARGS_SAVE_IMAGES,\n    CONFIG_FILE_KEY_MODEL_EXTENSION,\n    CONFIG_FILE_KEY_MODEL_NAME,\n    CONFIG_FILE_KEY_PACKAGE_LINK,\n    CONFIG_FILE_KEY_PACKAGE_NAME,\n    CONFIG_FILE_KEY_SELECTION,\n    CONFIG_FILE_NAME_AND_EXTENSION,\n    CONFIG_FILE_URL,\n)\nfrom .utils import Utils\n\n\nclass ConfigManager:\n    \"\"\"`ConfigManager` class: Downloads, loads and parses medigan's config json as dictionary.\n\n    Parameters\n    ----------\n    config_dict: dict\n        Optionally provides the config dictionary if already loaded and parsed in a previous process.\n    is_new_download_forced: bool\n        Flags, if True, that a new config file should be downloaded from the config link instead of parsing an existing\n        file.\n\n    Attributes\n    ----------\n    config_dict: dict\n        Optionally provides the config dictionary if already loaded and parsed in a previous process.\n    is_new_download_forced: bool\n        Flags, if True, that a new config file should be downloaded from the config link instead of parsing an existing\n        file.\n    model_ids: list\n        Lists the unique id's of the generative models specified in the `config_dict`\n    is_config_loaded: bool\n        Flags if the loading and parsing of the config file was successful (True) or not (False).\n    \"\"\"\n\n    def __init__(self, config_dict: dict = None, is_new_download_forced: bool = False):\n        self.config_dict = config_dict\n        self.model_ids = []\n        self.is_config_loaded = False\n        self.load_config_file(is_new_download_forced=is_new_download_forced)\n\n    def load_config_file(self, is_new_download_forced: bool = False) -> bool:\n        \"\"\"Load a config file and return boolean flag indicating success of loading process.\n\n        If the config file is not present in `medigan.CONSTANTS.CONFIG_FILE_FOLDER`, it is per default downloaded from\n        the web resource specified in `medigan.CONSTANTS.CONFIG_FILE_URL`.\n\n        Parameters\n        ----------\n        is_new_download_forced: bool\n            Forces new download of config file even if the file has been downloaded before.\n\n        Returns\n        -------\n        bool\n            a boolean flag indicating true only if the config file was loaded successfully.\n        \"\"\"\n        if self.config_dict is None:\n            assert Utils.mkdirs(\n                path_as_string=CONFIG_FILE_FOLDER\n            ), f\"The config folder was not found nor created in {CONFIG_FILE_FOLDER}.\"\n            config_file_path = Path(\n                f\"{CONFIG_FILE_FOLDER}/{CONFIG_FILE_NAME_AND_EXTENSION}\"\n            )\n            try:\n                if not Utils.is_file_located_or_downloaded(\n                    path_as_string=config_file_path,\n                    download_if_not_found=True,\n                    download_link=CONFIG_FILE_URL,\n                    is_new_download_forced=is_new_download_forced,\n                ):\n                    error_string = (\n                        f\"The config file {CONFIG_FILE_NAME_AND_EXTENSION} was not found in {config_file_path} \"\n                        f\"nor downloaded from {CONFIG_FILE_URL}.\"\n                    )\n                    logging.error(error_string)\n                    raise FileNotFoundError(error_string)\n            except Exception as e:\n                raise e\n            self.config_dict = Utils.read_in_json(path_as_string=config_file_path)\n            logging.debug(f\"The parsed config dict: {self.config_dict} \")\n            self.model_ids = [config for config in self.config_dict]\n            logging.debug(f\"The model_ids found in the config dict: {self.model_ids} \")\n            self.is_config_loaded = True\n        return self.is_config_loaded\n\n    def get_config_by_id(self, model_id: str, config_key: str = None) -> dict:\n        \"\"\"From `config_manager`, get and return the part of the config below a config_key for a specific `model_id`.\n\n        The key param can contain '.' (dot) separations to allow for retrieval of nested config keys such as\n        'execution.generator.name'\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        config_key: str\n            A key of interest present in the config dict\n\n        Returns\n        -------\n        dict\n            a dictionary from the part of the config file corresponding to `model_id` and `config_key`.\n        \"\"\"\n        config_dict = self.config_dict[model_id]\n        if config_key is not None:\n            # Split the key string by \".\" to enable evaluation of keys that are in nested dicts\n            config_key_split = config_key.split(\".\")\n            for key in config_key_split:\n                config_dict = config_dict[key]\n        return config_dict\n\n    def add_model_to_config(\n        self,\n        model_id: str,\n        metadata: dict,\n        is_local_model: bool = True,\n        overwrite_existing_metadata: bool = False,\n        store_new_config: bool = True,\n    ) -> bool:\n        \"\"\"Adding or updating a model entry in the global metadata.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        metadata: dict\n            The model's corresponding metadata\n        is_local_model: bool\n            flag indicating whether the tested model is a new local user model i.e not yet part of medigan's official models\n        overwrite_existing_metadata: bool\n            in case of `is_local_model`, flag indicating whether existing metadata for this model in medigan's `config/global.json` should be overwritten.\n        store_new_config: bool\n            flag indicating whether the current model metadata should be stored on disk i.e. in config/\n\n        Returns\n        -------\n        bool\n            Flag indicating whether model metadata update was successfully concluded\n        \"\"\"\n\n        if not self.is_model_metadata_valid(\n            model_id=model_id, metadata=metadata, is_local_model=is_local_model\n        ):\n            logging.debug(\n                f\"{model_id}: Metadata was not added to config. Reason: metadata was not valid. Please revise and try again.\"\n            )\n            return False\n        if (\n            self.is_model_in_config(model_id=model_id)\n            and not overwrite_existing_metadata\n        ):\n            logging.warning(\n                f\"{model_id}: Metadata was not added to config. Reason: For {model_id} there is already an entry in the metadata and 'overwrite_existing_metadata' was set to {overwrite_existing_metadata}.\"\n            )\n            return False\n        self.config_dict.update(metadata)\n        if store_new_config:\n            Utils.store_dict_as(\n                dictionary=self.config_dict,\n                output_path=f\"{CONFIG_FILE_FOLDER}/{CONFIG_FILE_NAME_AND_EXTENSION}\",\n            )\n            logging.info(\n                f\"{model_id}: Model metadata was added and config file ({CONFIG_FILE_FOLDER}/{CONFIG_FILE_NAME_AND_EXTENSION}) was successfully updated.\"\n            )\n        else:\n            logging.info(\n                f\"{model_id}: Model metadata was successfully added. Note: config file ({CONFIG_FILE_FOLDER}/{CONFIG_FILE_NAME_AND_EXTENSION}) was NOT updated.\"\n            )\n        return True\n\n    def is_model_in_config(self, model_id: str) -> bool:\n        \"\"\"Checking if a `model_id` is present in the global model metadata file\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n\n        Returns\n        -------\n        bool\n            Flag indicating whether a `model_id` is present in global model metadata\n        \"\"\"\n\n        try:\n            self.get_config_by_id(model_id)\n        except KeyError as e:\n            return False\n        return True\n\n    def is_model_metadata_valid(\n        self,\n        model_id: str,\n        metadata: dict,\n        is_local_model: bool = True,\n    ) -> bool:\n        \"\"\"Checking if a model's corresponding metadata is valid.\n\n        Specific fields in the model's metadata are mandatory. It is asserted if these key value pairs are present.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        metadata: dict\n            The model's corresponding metadata\n        is_local_model: bool\n            flag indicating whether the tested model is a new local user model i.e not yet part of medigan's official models\n\n        Returns\n        -------\n        bool\n            Flag indicating whether the specific model's metadata format and fields are valid\n        \"\"\"\n\n        try:\n            # Assert metadata not None and the existence of the most important entries of the metadata nested below model_id\n            assert (\n                metadata is not None\n            ), f\" {model_id}: Error validating metadata. metadata is None (metadata={metadata}).\"\n            assert (\n                metadata[model_id] is not None\n            ), f\" {model_id}: Error validating metadata. metadata does not contain model_id ({model_id}). (metadata={metadata}).\"\n            metadata = metadata[model_id]\n            expected_key_list = (CONFIG_FILE_KEY_EXECUTION, CONFIG_FILE_KEY_SELECTION)\n            assert all(\n                keys in metadata for keys in expected_key_list\n            ), f\"{model_id}: Error validating metadata. metadata did not contain one of '{expected_key_list}'. Metadata : {metadata}\"\n\n            # Checking entries inside 'execution' dict\n            metadata = metadata[CONFIG_FILE_KEY_EXECUTION]\n            expected_key_list = (\n                CONFIG_FILE_KEY_PACKAGE_LINK,\n                CONFIG_FILE_KEY_MODEL_EXTENSION,\n                CONFIG_FILE_KEY_PACKAGE_NAME,\n                CONFIG_FILE_KEY_MODEL_NAME,\n                CONFIG_FILE_KEY_DEPENDENCIES,\n                CONFIG_FILE_KEY_GENERATE,\n            )\n            assert all(\n                keys in metadata for keys in expected_key_list\n            ), f\"{model_id}: Error validating metadata. metadata did not contain one of '{expected_key_list}'. Metadata : {metadata}\"\n\n            # Checking if package name is present\n            package_name = metadata[CONFIG_FILE_KEY_PACKAGE_NAME]\n            assert (\n                package_name is not None and package_name != \"\"\n            ), f\"{model_id}: Error validating metadata. The package name ({package_name}) is either not defined or an empty string. Please revise.\"\n\n            package_path = Path(metadata[CONFIG_FILE_KEY_PACKAGE_LINK])\n            if is_local_model:\n                if not (package_path.exists() and package_path.is_file()):\n                    # TODO: Optional additional validation: If the package path actually points to a valid zip, check if the expected model file can be found in that zip.\n                    assert (\n                        package_path.exists() and package_path.is_dir()\n                    ), f\"{model_id}: Error validating metadata. Your package path ({package_path}) does not exist. Please revise and make sure the entry for metadata field '{CONFIG_FILE_KEY_PACKAGE_LINK}' points to a valid folder or zip file containing your model.\"\n            else:\n                assert Utils.is_url_valid(\n                    the_url=package_path\n                ), f\"{model_id}: Error validating metadata. The package_path is not a valid url {package_path}. Please revise.\"\n\n            # checking entries inside 'execution.generate_method' dict\n            metadata = metadata[CONFIG_FILE_KEY_GENERATE]\n            assert (\n                CONFIG_FILE_KEY_GENERATE_ARGS in metadata\n            ), f\"{model_id}: Error validating metadata. It did not contain key '{CONFIG_FILE_KEY_GENERATE_ARGS}'. Metadata: {metadata}\"\n\n            # checking entries inside 'execution.generate_method.args' dict\n            metadata = metadata[CONFIG_FILE_KEY_GENERATE_ARGS]\n            assert (\n                CONFIG_FILE_KEY_GENERATE_ARGS_BASE in metadata\n            ), f\"{model_id}: Error validating metadata. It did not contain key '{CONFIG_FILE_KEY_GENERATE_ARGS_BASE}'. Metadata: {metadata}\"\n\n            # checking entries inside 'execution.generate_method.args.base' dict\n            metadata = metadata[CONFIG_FILE_KEY_GENERATE_ARGS_BASE]\n            expected_key_list = (\n                CONFIG_FILE_KEY_GENERATE_ARGS_MODEL_FILE,\n                CONFIG_FILE_KEY_GENERATE_ARGS_NUM_SAMPLES,\n                CONFIG_FILE_KEY_GENERATE_ARGS_OUTPUT_PATH,\n                CONFIG_FILE_KEY_GENERATE_ARGS_SAVE_IMAGES,\n            )\n            assert all(\n                keys in metadata for keys in expected_key_list\n            ), f\"{model_id}: Error validating metadata. metadata did not contain one of '{expected_key_list}'. Metadata : {metadata}\"\n        except Exception as e:\n            logging.info(f\"Metadata for model '{model_id}' was not valid: {e}\")\n            return False\n        return True\n\n    def match_model_id(self, provided_model_id: str) -> bool:\n        \"\"\"Replacing a model_id acronym (e.g. 00005 or 5) with the unique `model_id` present in the model metadata\n\n        Parameters\n        ----------\n        provided_model_id: str\n            The user-provided model_id that might be shorter (e.g. \"00005\" or \"5\") than the real unique model id\n\n        Returns\n        -------\n        str\n            If matched, returning the unique `model_id` present in global model metadata.\n        \"\"\"\n\n        # (1) quick check if model_id is in config. In this case no matching is needed.\n        # (2) if the model id's length is >5, it comprises info different from the numeric id (e.g., 00001) and could\n        # be ambiguous (e.g. PGGAN_CHEST).\n        p_id_length = len(str(provided_model_id))\n        if (\n            not self.is_model_in_config(model_id=str(provided_model_id))\n            and p_id_length <= 5\n            and p_id_length > 0\n        ):\n            for i in range(5 - p_id_length):\n                # Adding zeros e.g., to allow matching and to avoid returning 01015 instead of 00015.\n                provided_model_id = \"0\" + str(provided_model_id)\n            for model_id in self.model_ids:\n                if model_id[0:5] == str(provided_model_id):\n                    logging.debug(\n                        f\"model_id[0:5]={model_id[0:5]}, provided_model_id={provided_model_id}. Matched: {model_id}\"\n                    )\n                    return model_id\n        return provided_model_id\n\n    def __str__(self):\n        return json.dumps(self.config_dict)\n\n    def __repr__(self):\n        return f\"ConfigManager(model_ids={self.model_ids}, is_config_loaded={self.is_config_loaded})\"\n\n    def __len__(self):\n        return len(self.config_dict)\n\n    def __getitem__(self, idx: int):\n        return list(self.config_dict)[idx]\n"
  },
  {
    "path": "src/medigan/constants.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" Global constants of the medigan library \"\"\"\n\n\"\"\" Folder path that will be created to locally store the model modules. \"\"\"\nMODEL_FOLDER = \"models\"\n\n\"\"\" Static link to the config of medigan. Note: To add a model, please create pull request in this github repo. \"\"\"\nCONFIG_FILE_URL = (\n    \"https://raw.githubusercontent.com/RichardObi/medigan/main/config/global.json\"\n)\n\n\"\"\" Folder path that will be created to locally store the config file. \"\"\"\nCONFIG_FILE_FOLDER = \"config\"\n\n\"\"\" Name and extensions of config file. \"\"\"\nCONFIG_FILE_NAME_AND_EXTENSION = \"global.json\"\n\n\"\"\" The key under which the execution dictionary of a model is nested in the config file. \"\"\"\nCONFIG_FILE_KEY_EXECUTION = \"execution\"\n\n\"\"\" The key under which the selection dictionary of a model is nested in the config file. \"\"\"\nCONFIG_FILE_KEY_SELECTION = \"selection\"\n\n\"\"\" The key under which the description dictionary of a model is nested in the config file. \"\"\"\nCONFIG_FILE_KEY_DESCRIPTION = \"description\"\n\n\"\"\" Below the selection dict, the key under which the performance dictionary of a model is nested in the config file. \"\"\"\nCONFIG_FILE_KEY_PERFORMANCE = \"performance\"\n\n\"\"\" Below the execution dict, the key under which the dependencies dictionary of a model is nested in the config file. \"\"\"\nCONFIG_FILE_KEY_DEPENDENCIES = \"dependencies\"\n\n\"\"\" Below the execution dict, the key under which the package link of a model is present in the config file. \nNote: The model packages are per convention stored on Zenodo where they retrieve a static DOI avoiding security issues \ndue to static non-modifiable content on Zenodo. Zenodo also helps to maintain clarity of who the owners and contributors\nof each generative model (and its IP) in medigan are. \"\"\"\nCONFIG_FILE_KEY_PACKAGE_LINK = \"package_link\"\n\n\"\"\" Below the execution dict, the key under which the extension of a model is present in the config file. \"\"\"\nCONFIG_FILE_KEY_MODEL_EXTENSION = \"extension\"\n\n\"\"\" Below the execution dict, the key under which the package_name of a model is present in the config file. \"\"\"\nCONFIG_FILE_KEY_PACKAGE_NAME = \"package_name\"\n\n\"\"\" Below the execution dict, the key under which the package_name of a model is present in the config file. \"\"\"\nCONFIG_FILE_KEY_GENERATOR = \"generator\"\n\n\"\"\" Below the execution dict, the key under which a model's generator's is present in the config file. \"\"\"\nCONFIG_FILE_KEY_GENERATOR_NAME = \"name\"\n\n\"\"\" Below the execution dict, the key under which a model's image_size is present in the config file. \"\"\"\nCONFIG_FILE_KEY_IMAGE_SIZE = \"image_size\"\n\n\"\"\" Below the execution dict, the key under which a model's name is present in the config file. This is the name of the weights file! \"\"\"\nCONFIG_FILE_KEY_MODEL_NAME = (\n    \"model_name\"  # TODO: Rename to something like \"model_weights_name\"\n)\n\n\"\"\" Below the execution dict, the key under which a nested dict with info on the model's generate() function is present. \"\"\"\nCONFIG_FILE_KEY_GENERATE = \"generate_method\"\n\n\"\"\" Below the execution dict, the key under which the exact name of a model's generate() function is present. \"\"\"\nCONFIG_FILE_KEY_GENERATE_NAME = \"name\"\n\n\"\"\" Below the execution dict, the key under which a nested dict with info on the arguments of a model's generate() function is present. \"\"\"\nCONFIG_FILE_KEY_GENERATE_ARGS = \"args\"\n\n\"\"\" Below the execution dict, the key under which an array of mandatory base arguments of any model's generate() function is present. \"\"\"\nCONFIG_FILE_KEY_GENERATE_ARGS_BASE = \"base\"\n\n\"\"\" Below the execution dict, the key under which a nested dict of key-value pairs of model specific custom arguments of a model's generate() function are present. \"\"\"\nCONFIG_FILE_KEY_GENERATE_ARGS_CUSTOM = \"custom\"\n\n\"\"\" Below the execution dict, the key under which the model_file argument value of any model's generate() function is present. \"\"\"\nCONFIG_FILE_KEY_GENERATE_ARGS_MODEL_FILE = \"model_file\"\n\n\"\"\" Below the execution dict, the key under which the num_samples argument value of any model's generate() function is present. \"\"\"\nCONFIG_FILE_KEY_GENERATE_ARGS_NUM_SAMPLES = \"num_samples\"\n\n\"\"\" Below the execution dict, the key under which the output_path argument value of any model's generate() function is present. \"\"\"\nCONFIG_FILE_KEY_GENERATE_ARGS_OUTPUT_PATH = \"output_path\"\n\n\"\"\" Below the execution dict, the key under which the save images boolean flag argument value of any model's generate() function is present. \"\"\"\nCONFIG_FILE_KEY_GENERATE_ARGS_SAVE_IMAGES = \"save_images\"\n\n\"\"\" Below the execution dict, the key under which the random input_latent_vector_size argument value of model's generate() function is present. \"\"\"\nCONFIG_FILE_KEY_GENERATE_ARGS_INPUT_LATENT_VECTOR_SIZE = \"input_latent_vector_size\"\n\n\"\"\" Below the selectoin dict, the key under which the tags (list of strings) is present. \"\"\"\nCONFIG_FILE_KEY_TAGS = \"tags\"\n\n\"\"\" The filetype of any of the generative model's python packages after download and before unpacking. \"\"\"\nPACKAGE_EXTENSION = \".zip\"\n\n\"\"\" The string describing a model's unique id in medigan's data structures. \"\"\"\nMODEL_ID = \"model_id\"\n\n\"\"\" The default path to a folder under which the outputs of the medigan package (i.e. generated samples) are stored. \"\"\"\nDEFAULT_OUTPUT_FOLDER = \"output\"\n\n\"\"\" The folder containing an __init__.py file is a python module. \"\"\"\nINIT_PY_FILE = \"__init__.py\"\n\n\"\"\" Name and extensions of template of config file. \"\"\"\nCONFIG_TEMPLATE_FILE_NAME_AND_EXTENSION = \"template.json\"\n\n\"\"\" Download link to template.json file. \"\"\"\nCONFIG_TEMPLATE_FILE_URL = (\n    \"https://raw.githubusercontent.com/RichardObi/medigan/main/templates/template.json\"\n)\n\n\"\"\" Name and extensions of template of config file. \"\"\"\nTEMPLATE_FOLDER = \"templates\"\n\n\"\"\" The line break in the Zenodo description that appears together with the pushed model on Zenodo\"\"\"\nZENODO_LINE_BREAK = \"<p>&nbsp;</p>\"\n\n\"\"\" A generic description appended to model uploads that are automatically uploaded to zenodo via Zenodo API call in medigan\"\"\"\nZENODO_GENERIC_MODEL_DESCRIPTION = (\n    f\"<p><strong>Usage:</strong></p> <p>This GAN is used as part of&nbsp;the <strong><em>medigan</em></strong> library. \"\n    f\"This GANs metadata is therefore stored in and retrieved from&nbsp;<em>medigan&#39;s</em> \"\n    f\"<a href='https://raw.githubusercontent.com/RichardObi/medigan/main/config/global.json'>config&nbsp;file</a>.&nbsp;<em>medigan </em>\"\n    f\"is an open-source Python&nbsp;library&nbsp;on <a href='https://github.com/RichardObi/medigan'>Github</a> that allows developers and \"\n    f\"researchers to easily add synthetic imaging data&nbsp;into their model training pipelines. <em>medigan</em> is documented \"\n    f\"<a href='https://readthedocs.org/projects/medigan/'>here</a> and can be used via pip install:</p> \"\n    f\"<pre><code class='language-python'>pip install medigan</code></pre> <p>To run this model in medigan,&nbsp;use the following commands.</p> \"\n    f\"<pre> <code class='language-python'> from medigan import Generators  </code></pre>\"\n    f\"<pre> <code class='language-python'> generators = Generators() </code></pre>\"\n    f\"<pre> <code class='language-python'> generators.generate(model_id='YOUR_MODEL_ID',num_samples=10)</code></pre><p>&nbsp;</p>\"\n)\n\n\"\"\" The REST API to interact with Zenodo \"\"\"\nZENODO_API_URL = \"https://zenodo.org/api/deposit/depositions\"  # \"https://sandbox.zenodo.org/api/deposit/depositions\"\n\n\"\"\" The HEADER for Zenodo REST API requests\"\"\"\nZENODO_HEADERS = {\"Content-Type\": \"application/json\"}\n\n\"\"\" The title of the Github Issue when adding a model to medigan\"\"\"\nGITHUB_TITLE = \"Model Integration Request for medigan\"\n\n\"\"\" The repository of the Github Issue when adding a model to medigan\"\"\"\nGITHUB_REPO = \"RichardObi/medigan\"  # \"RichardObi/medigan-models\"\n\n\"\"\" The assignee of the Github Issue when adding a model to medigan\"\"\"\nGITHUB_ASSIGNEE = \"RichardObi\"\n"
  },
  {
    "path": "src/medigan/contribute_model/__init__.py",
    "content": ""
  },
  {
    "path": "src/medigan/contribute_model/base_model_uploader.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\"Base Model uploader class that uploads models to medigan associated data storage services. \"\"\"\n\nfrom __future__ import absolute_import\n\n\nclass BaseModelUploader:\n    \"\"\"`BaseModelUploader` class: Uploads a user's model and metadata to third party storage to allow its inclusion into the medigan library.\n\n    Parameters\n    ----------\n    model_id: str\n        The generative model's unique id\n    metadata: dict\n        The model's corresponding metadata\n\n    Attributes\n    ----------\n    model_id: str\n        The generative model's unique id\n    metadata: dict\n        The model's corresponding metadata\n    \"\"\"\n\n    def __init__(\n        self,\n        model_id: str,\n        metadata: dict,\n    ):\n        self.model_id = model_id\n        self.metadata = metadata\n\n    def push(self):\n        raise NotImplementedError\n\n    def __repr__(self):\n        return f\"BaseModelUploader(model_id={self.model_id}, metadata={self.metadata})\"\n\n    def __len__(self):\n        raise NotImplementedError\n\n    def __getitem__(self, idx: int):\n        raise NotImplementedError\n"
  },
  {
    "path": "src/medigan/contribute_model/github_model_uploader.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\"Github Model uploader class that uploads the metadata of a new model to the medigan github repository. \"\"\"\n\nfrom __future__ import absolute_import\n\nimport json\nimport logging\n\nfrom github import Github\n\nfrom ..constants import (\n    CONFIG_FILE_KEY_EXECUTION,\n    CONFIG_FILE_KEY_PACKAGE_LINK,\n    GITHUB_ASSIGNEE,\n    GITHUB_REPO,\n    GITHUB_TITLE,\n)\nfrom ..utils import Utils\nfrom .base_model_uploader import BaseModelUploader\n\n\nclass GithubModelUploader(BaseModelUploader):\n    \"\"\"`GithubModelUploader` class: Pushes the metadata of a user's model to the medigan repo, where it creates a\n    dedicated github issue.\n\n    Parameters\n    ----------\n    model_id: str\n        The generative model's unique id\n    access_token: str\n        a personal access token linked to your github user account, used as means of authentication\n\n    Attributes\n    ----------\n    model_id: str\n        The generative model's unique id\n    access_token: str\n        a personal access token linked to your github user account, used as means of authentication\n    \"\"\"\n\n    def __init__(\n        self,\n        model_id: str,\n        access_token: str,\n    ):\n        self.model_id = model_id\n        self.access_token = access_token\n\n    def push(\n        self,\n        metadata: dict,\n        package_link: str = None,\n        creator_name: str = \"n.a.\",\n        creator_affiliation: str = \"n.a.\",\n        model_description: str = \"n.a.\",\n    ):\n        \"\"\"Upload the model's metadata inside a github issue to the medigan github repository.\n\n        To add your model to medigan, your metadata will be reviewed on Github and added to medigan's official model metadata\n\n        The medigan repository issues page: https://github.com/RichardObi/medigan/issues\n\n        Get your Github access token here: https://github.com/settings/tokens\n\n        Parameters\n        ----------\n        metadata: dict\n            The model's corresponding metadata\n        package_link:\n            a package link\n        creator_name: str\n            the creator name that will appear on the corresponding github issue\n        creator_affiliation: str\n            the creator affiliation that will appear on the corresponding github issue\n        model_description: list\n            the model_description that will appear on the corresponding github issue\n\n        Returns\n        -------\n        str\n            Returns the url pointing to the corresponding issue on github\n        \"\"\"\n\n        # Check if the package_link is already in the metadata. If not, add it to metadata.\n        metadata = self.add_package_link_to_metadata(\n            metadata=metadata, package_link=package_link\n        )\n\n        # First use pyGithub to create a Github instance based on san access token\n        g = Github(self.access_token)\n        repo = g.get_repo(GITHUB_REPO)\n        logging.debug(f\"Repo: {repo}\")\n\n        # Create metadata for github issue\n        title = f\"{GITHUB_TITLE}: {self.model_id}\"\n        line_break = \"\\n\"\n        body = (\n            f\"{line_break + '**Creator:** ' if creator_name != '' else ''}{creator_name}\"\n            f\"{line_break + '**Affiliation:** ' if creator_affiliation != '' else ''}{creator_affiliation}\"\n            f\"{line_break + '**Description:** ' if model_description != '' else ''}{model_description}\"\n            f\"{line_break} **Stored in:** {package_link}\"\n            f\"{line_break} ### Model Metadata: {line_break} ```json{json.dumps(metadata, indent=3)}\"\n        )\n\n        # As a logged-in github user, let's now push to medigan repo using pyGithub\n        github_issue = repo.create_issue(\n            title=title, body=body, assignee=GITHUB_ASSIGNEE\n        )\n        logging.info(\n            f\"{self.model_id}: Successfully created github issue in '{GITHUB_REPO}': '{github_issue.html_url}'\"\n        )\n        return github_issue.html_url\n\n    def add_package_link_to_metadata(\n        self, metadata: dict, package_link: str = None, is_update_forced: bool = False\n    ) -> dict:\n        \"\"\"Update `package_link` in the model's metadata if current `package_link` does not containing a valid url.\n\n        Parameters\n        ----------\n        metadata: dict\n            The model's corresponding metadata\n        package_link: str\n            the new package link to used to replace the old one\n        is_update_forced: bool\n            flag to update metadata even though metadata already contains a valid url in its `package_link`\n\n        Returns\n        -------\n        dict\n            Returns the updated metadata dict with replaced `package_link` if replacement was applicable.\n        \"\"\"\n\n        current_pl = None\n        try:\n            # Get the package link from the metadata object\n            current_pl = metadata[self.model_id][CONFIG_FILE_KEY_EXECUTION][\n                CONFIG_FILE_KEY_PACKAGE_LINK\n            ]\n        except Exception as e:\n            logging.debug(\n                f\"{self.model_id}: Package Link could not be located in metadata for key {self.model_id}.{CONFIG_FILE_KEY_EXECUTION}.{CONFIG_FILE_KEY_PACKAGE_LINK}: {e}\"\n            )\n\n        # Check if the package link in the metadata contains a valid URL\n        if (\n            current_pl is not None\n            and not is_update_forced\n            and (\n                (Utils.is_url_valid(current_pl) and current_pl.startswith(\"http\"))\n                or current_pl.startswith(\"models/\")\n            )\n        ):\n            # If there is already a valid (non-local) url to a zip file,\n            # we assume that this URL is validly pointing to the model.\n            # Note: The package link can start with models/ indicating that the model is hosted directly in medigan\n            # instead of Zenodo, see model 00007 for an example.\n            pass\n        else:\n            # We update the metadata with the retrieved package_link. Note: Also, in case the metadata points to a path on a\n            # user's machine, we avoid publishing that path to github issue by making this update.\n            try:\n                metadata[self.model_id][CONFIG_FILE_KEY_EXECUTION][\n                    CONFIG_FILE_KEY_PACKAGE_LINK\n                ] = package_link\n            except Exception as e:\n                logging.warning(\n                    f\"{self.model_id}: Package Link could not be update in metadata for key {self.model_id}.{CONFIG_FILE_KEY_EXECUTION}.{CONFIG_FILE_KEY_PACKAGE_LINK}: {e}\"\n                )\n            logging.debug(\n                f\"{self.model_id}: Before creating github issue, updated package link from '{current_pl}' to '{package_link}'\"\n            )\n        return metadata\n\n    def __repr__(self):\n        return (\n            f\"GithubModelUploader(model_id={self.model_id}, metadata={self.metadata})\"\n        )\n\n    def __len__(self):\n        raise NotImplementedError\n\n    def __getitem__(self, idx: int):\n        raise NotImplementedError\n"
  },
  {
    "path": "src/medigan/contribute_model/model_contributor.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\"Model contributor class that tests models, creates metadata entries, uploads and contributes them to medigan. \"\"\"\n\nfrom __future__ import absolute_import\n\nimport importlib\nimport logging\nimport sys\nfrom pathlib import Path\n\nfrom ..constants import (\n    CONFIG_FILE_KEY_DEPENDENCIES,\n    CONFIG_FILE_KEY_EXECUTION,\n    CONFIG_FILE_KEY_GENERATE,\n    CONFIG_FILE_KEY_GENERATE_NAME,\n    CONFIG_FILE_KEY_MODEL_EXTENSION,\n    CONFIG_FILE_KEY_MODEL_NAME,\n    CONFIG_FILE_KEY_PACKAGE_LINK,\n    CONFIG_FILE_KEY_PACKAGE_NAME,\n    CONFIG_TEMPLATE_FILE_NAME_AND_EXTENSION,\n    CONFIG_TEMPLATE_FILE_URL,\n    INIT_PY_FILE,\n    TEMPLATE_FOLDER,\n)\nfrom ..utils import Utils\nfrom .github_model_uploader import GithubModelUploader\nfrom .zenodo_model_uploader import ZenodoModelUploader\n\n\nclass ModelContributor:\n    \"\"\"`ModelContributor` class: Contributes a user's local model to the public medigan library\n\n    Parameters\n    ----------\n    model_id: str\n        The generative model's unique id\n    init_py_path: str\n        The path to the local model's `__init__.py` file needed for importing and running this model.\n\n    Attributes\n    ----------\n    model_id: str\n        The generative model's unique id\n    init_py_path: str\n        The path to the local model's __init__.py file needed for importing and running this model.\n    package_path: str\n        Path as string to the generative model's python package\n    package_name: str\n        Name of the model's python package i.e. the name of the model's zip file and unzipped package folder\n    metadata_file_path: str\n        Path as string to the generative model's metadata file e.g. default is relative path to package root.\n    zenodo_model_uploader: str\n        An instance of the `ZenodoModelUploader` class\n    github_model_uploader: str\n        An instance of the `GithubModelUploader` class.\n    \"\"\"\n\n    def __init__(\n        self,\n        model_id: str,\n        init_py_path: str,\n    ):\n        self.validate_model_id(model_id)\n        self.model_id = model_id\n        self.init_py_path = init_py_path\n        self.validate_init_py_path(init_py_path)\n        self.package_path = self.init_py_path.replace(INIT_PY_FILE, \"\")\n        self.package_name = Path(self.package_path).name\n        self.metadata_file_path = \"\"  # Default is relative path to package root.\n        self.validate_local_model_import()\n        self.zenodo_model_uploader = None\n        self.github_model_uploader = None\n\n    ############################ VALIDATION ############################\n\n    def validate_model_id(\n        self, model_id: str, max_chars: int = 30, min_chars: int = 13\n    ) -> bool:\n        \"\"\"Asserts if the `model_id` is in the correct format and has a valid length\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        max_chars: int\n            the maximum of chars allowed in the model_id\n        min_chars: int\n            the minimum of chars allowed in the model_id\n\n        Returns\n        -------\n        bool\n            Returns flag indicating whether the `model_id` is correctly formatted.\n        \"\"\"\n\n        num_chars = len(model_id)\n        assert (\n            num_chars <= max_chars\n        ), f\"The model_id {model_id} is too large ({num_chars}). Please reduce to a maximum of {max_chars} characters. Format Convention: '00001_GANTYPE_MODALITY'\"\n        assert (\n            num_chars >= min_chars\n        ), f\"The model_id {model_id} is too small ({num_chars}). Please reduce to a minimum of {min_chars} characters. Format Convention: '00001_GANTYPE_MODALITY'\"\n        for i in range(5):\n            assert model_id[\n                i\n            ].isdigit(), f\"Your model_id's ({model_id}) character '{model_id[i]}' at position {i} is not a digit. The first 5 characters should be digits as in '00001_GANTYPE_MODALITY'. Please adjust.\"\n        logging.info(\n            f\"The provided model_id is valid and will now be used to refer to the contributed model in medigan: {model_id}\"\n        )\n        return True\n\n    def validate_init_py_path(self, init_py_path) -> bool:\n        \"\"\"Asserts whether the `init_py_path` exists and points to a valid `__init__.py` correct file.\n\n        Parameters\n        ----------\n        init_py_path: str\n            The path to the local model's __init__.py file needed for importing and running this model.\n        \"\"\"\n\n        assert (\n            Path(init_py_path).exists() and Path(init_py_path).is_file()\n        ), f\"{self.model_id}: The path to your model's __init__.py function does not exist or does not point to a file. Please revise path {init_py_path}. Note: You can find an __init__.py example in https://github.com/RichardObi/medigan/tree/main/templates\"\n        assert Utils.is_file_in(\n            folder_path=self.init_py_path.replace(f\"/{INIT_PY_FILE}\", \"\"),\n            filename=INIT_PY_FILE,\n        ), f\"{self.model_id}: No __init__.py was found in your path {init_py_path}. Please revise. Note: You can find an __init__.py example in /templates in https://github.com/RichardObi/medigan\"\n        logging.info(\n            f\"The provided path to your model's __init__.py function was valid and points to a __init__.py file: {init_py_path}\"\n        )\n        return True\n\n    def validate_and_update_model_weights_path(self) -> dict:\n        \"\"\"Check if the model files can be found in the `package_path` or based on the `path_to_metadata`.\n\n        Ideally, the user provided `package_path` and the `path_to_metadata` should both point to the same model package\n        containing weights, config, license, etc. Here we check both of these paths to find the model weights.\n\n        Returns\n        -------\n        dict\n            Returns the metadata after updating the path to the model's checkpoint's weights\n        \"\"\"\n\n        metadata_dir_path = Path(self.metadata_file_path).parent\n\n        potential_weight_paths: list = []\n\n        execution_metadata = self.metadata[self.model_id][CONFIG_FILE_KEY_EXECUTION]\n\n        # package_path + package_path + file + extension\n        try:\n            potential_weight_paths.append(\n                Path(\n                    self.package_path\n                    + f\"/{execution_metadata[CONFIG_FILE_KEY_MODEL_NAME]}{execution_metadata[CONFIG_FILE_KEY_MODEL_EXTENSION]}\"\n                )\n            )\n        except KeyError as e:\n            raise e\n\n        # metadata_dir + package_path + file + extension\n        try:\n            potential_weight_paths.append(\n                Path(\n                    str(metadata_dir_path)\n                    + f\"/{execution_metadata[CONFIG_FILE_KEY_MODEL_NAME]}{execution_metadata[CONFIG_FILE_KEY_MODEL_EXTENSION]}\"\n                )\n            )\n        except KeyError as e:\n            raise e\n\n        # metadata_dir + package_path + file + extension\n        try:\n            potential_weight_paths.append(\n                Path(\n                    str(metadata_dir_path)\n                    + \"/\"\n                    + self.package_path\n                    + f\"/{execution_metadata[CONFIG_FILE_KEY_MODEL_NAME]}{execution_metadata[CONFIG_FILE_KEY_MODEL_EXTENSION]}\"\n                )\n            )\n        except KeyError as e:\n            raise e\n\n        for potential_weight_path in potential_weight_paths:\n            if potential_weight_path.is_file():\n                # Checking if there is a weights/checkpoint (model name + extension) file in the package /metadata path\n                self.package_path = str(\n                    Path(potential_weight_path).parent.resolve(strict=False)\n                )  # strict=False, as models might be not on user's disc.\n                self.metadata[self.model_id][CONFIG_FILE_KEY_EXECUTION][\n                    CONFIG_FILE_KEY_PACKAGE_LINK\n                ] = self.package_path\n                logging.info(\n                    f\"The model weights path is valid and was added to the metadata of your model: {self.package_path}\"\n                )\n                return self.metadata\n        raise FileNotFoundError(\n            f\"{self.model_id}: Error validating metadata. There was no valid model weights file found. Please revise. Tested paths: '{potential_weight_paths}'\"\n        )\n\n    def validate_local_model_import(self):\n        \"\"\"Check if the model package in the `package_path` can be imported as python library using importlib.\"\"\"\n\n        # Validation: Import module as python library to check if generate function is inside the\n        # path_to_script_w_generate_function python file and no errors occur.\n        try:\n            sys.path.insert(1, str(self.package_path).replace(self.package_name, \"\"))\n            importlib.import_module(name=self.package_name)\n            logging.info(\n                f\"Model import test successful: The model was successfully imported using importlib: {self.package_name}\"\n            )\n        except Exception as e:\n            raise Exception(\n                f\"{self.model_id}: Error while testing importlib model import. Is your {INIT_PY_FILE} erroneous? \"\n                f\"Please revise if the provided path ({self.init_py_path}) is valid and accessible and try again. \"\n                f\"Exception: {e}\"\n            ) from e\n\n    ############################ UPLOAD ############################\n\n    def push_to_zenodo(\n        self,\n        access_token: str,\n        creator_name: str,\n        creator_affiliation: str,\n        model_description: str = \"\",\n    ):\n        \"\"\"Upload the model files as zip archive to a public Zenodo repository where the model will be persistently stored.\n\n        Get your Zenodo access token here: https://zenodo.org/account/settings/applications/tokens/new/ (Enable scopes `deposit:actions` and `deposit:write`)\n\n        Parameters\n        ----------\n        access_token: str\n            a personal access token in Zenodo linked to a user account for authentication\n        creator_name: str\n            the creator name that will appear on the corresponding Zenodo model upload homepage\n        creator_affiliation: str\n            the creator affiliation that will appear on the corresponding Zenodo model upload homepage\n        model_description: list\n            the model_description that will appear on the corresponding Zenodo model upload homepage\n\n        Returns\n        -------\n        str\n            Returns the url pointing to the corresponding Zenodo model upload homepage\n        \"\"\"\n\n        if self.zenodo_model_uploader is None:\n            self.zenodo_model_uploader = ZenodoModelUploader(\n                model_id=self.model_id, access_token=access_token\n            )\n        # Update in case previous access token gave an error\n        self.zenodo_model_uploader.access_token = access_token\n\n        return self.zenodo_model_uploader.push(\n            metadata=self.metadata,\n            package_path=self.package_path,\n            package_name=self.package_name,\n            creator_name=creator_name,\n            creator_affiliation=creator_affiliation,\n            model_description=model_description,\n        )\n\n    def push_to_github(\n        self,\n        access_token: str,\n        package_link: str = None,\n        creator_name: str = \"\",\n        creator_affiliation: str = \"\",\n        model_description: str = \"\",\n    ):\n        \"\"\"Upload the model's metadata inside a github issue to the medigan github repository.\n\n        To add your model to medigan, your metadata will be reviewed on Github and added to medigan's official model metadata\n\n        The medigan repository issues page: https://github.com/RichardObi/medigan/issues\n\n        Get your Github access token here: https://github.com/settings/tokens\n\n        Parameters\n        ----------\n        access_token: str\n            a personal access token linked to your github user account, used as means of authentication\n        package_link:\n            a package link\n        creator_name: str\n            the creator name that will appear on the corresponding github issue\n        creator_affiliation: str\n            the creator affiliation that will appear on the corresponding github issue\n        model_description: list\n            the model_description that will appear on the corresponding github issue\n\n        Returns\n        -------\n        str\n            Returns the url pointing to the corresponding issue on github\n        \"\"\"\n\n        if self.github_model_uploader is None:\n            self.github_model_uploader = GithubModelUploader(\n                model_id=self.model_id, access_token=access_token\n            )\n        # Update in case previous access token gave an error\n        self.github_model_uploader.access_token = access_token\n\n        return self.github_model_uploader.push(\n            metadata=self.metadata,\n            package_link=package_link,\n            creator_name=creator_name,\n            creator_affiliation=creator_affiliation,\n            model_description=model_description,\n        )\n\n    ############################ METADATA ############################\n\n    def load_metadata_template(self) -> dict:\n        \"\"\"Loads and parses (json to dict) a default medigan metadata template.\n\n        Returns\n        -------\n        dict\n            Returns the metadata template as dict\n        \"\"\"\n\n        path_to_metadata_template = Path(\n            f\"{TEMPLATE_FOLDER}/{CONFIG_TEMPLATE_FILE_NAME_AND_EXTENSION}\"\n        )\n        Utils.mkdirs(TEMPLATE_FOLDER)\n        Utils.is_file_located_or_downloaded(\n            download_link=CONFIG_TEMPLATE_FILE_URL,\n            path_as_string=path_to_metadata_template,\n        )\n        metadata_template = Utils.read_in_json(path_as_string=path_to_metadata_template)\n        if self.model_id is not None:\n            # Replacing the placeholder id of template with model_id\n            metadata_template[self.model_id] = metadata_template[\n                list(metadata_template)[0]\n            ]\n            del metadata_template[list(metadata_template)[0]]\n        return metadata_template\n\n    def add_metadata_from_file(self, metadata_file_path) -> dict:\n        \"\"\"Read and parse the metadata of a local model, identified by `model_id`, from a metadata file in json format.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        metadata_file_path: str\n            the path pointing to the metadata file\n\n        Returns\n        -------\n        dict\n            Returns a dict containing the contents of parsed metadata json file.\n        \"\"\"\n\n        if Path(metadata_file_path).is_file():\n            self.metadata = Utils.read_in_json(path_as_string=metadata_file_path)\n            self.metadata_file_path = metadata_file_path\n        elif Path(metadata_file_path + \"/metadata.json\").is_file():\n            self.metadata = Utils.read_in_json(\n                path_as_string=metadata_file_path + \"/metadata.json\"\n            )\n            self.metadata_file_path = metadata_file_path + \"/metadata.json\"\n        else:\n            raise FileNotFoundError(\n                f\"{self.model_id}: No metadata json file was found in the path you provided ({metadata_file_path}). \"\n                f\"If you do not have a metadata file, create one using the add_metadata_from_input() function.\"\n            )\n        self.validate_and_update_model_weights_path()\n        return self.metadata\n\n    def add_metadata_from_input(\n        self,\n        model_weights_name: str = None,\n        model_weights_extension: str = None,\n        generate_method_name: str = None,\n        dependencies: list = [],\n        fill_more_fields_interactively: bool = True,\n        output_path: str = \"config\",\n    ):\n        \"\"\"Create a metadata dict for a local model, identified by `model_id`, given the necessary minimum metadata contents.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        model_weights_name: str\n            the name of the checkpoint file containing the model's weights\n        model_weights_extension: str\n            the extension (e.g. .pt) of the checkpoint file containing the model's weights\n        generate_method_name: str\n            the name of the sample generation method inside the models __init__.py file\n        dependencies: list\n            the list of dependencies that need to be installed via pip to run the model.\n        fill_more_fields_interactively: bool\n            flag indicating whether a user will be interactively asked via command line for further input to fill out missing metadata content\n        output_path: str\n            the path where the created metadata json file will be stored.\n\n        Returns\n        -------\n        dict\n            Returns a dict containing the contents of the metadata json file.\n        \"\"\"\n\n        # Get the metadata template to guide data structure and formatting of metadata.\n        self.metadata_template = self.load_metadata_template()\n\n        # Generate metadata with variables provided as parameters\n        metadata = self.metadata_template[self.model_id][CONFIG_FILE_KEY_EXECUTION]\n        metadata.update({CONFIG_FILE_KEY_PACKAGE_LINK: self.package_path})\n        metadata.update({CONFIG_FILE_KEY_PACKAGE_NAME: self.package_name})\n        metadata.update({CONFIG_FILE_KEY_MODEL_NAME: model_weights_name})\n        metadata.update({CONFIG_FILE_KEY_MODEL_EXTENSION: model_weights_extension})\n        metadata.update({CONFIG_FILE_KEY_DEPENDENCIES: dependencies})\n        metadata[CONFIG_FILE_KEY_GENERATE][\n            CONFIG_FILE_KEY_GENERATE_NAME\n        ] = generate_method_name\n        metadata_final = self.metadata_template\n        metadata_final[self.model_id].update({CONFIG_FILE_KEY_EXECUTION: metadata})\n\n        Utils.store_dict_as(\n            dictionary=metadata_final,\n            extension=\".json\",\n            output_path=output_path,\n            filename=self.model_id,\n        )\n        logging.info(\n            f\"{self.model_id}: Your model's metadata was stored in {output_path}.\"\n        )\n\n        if fill_more_fields_interactively:\n            # Add more information to the metadata dict via user prompts\n            metadata_final = self._recursively_fill_metadata(metadata=metadata_final)\n            # Store again as additional fields should have now been filled\n            Utils.store_dict_as(\n                dictionary=metadata_final,\n                extension=\".json\",\n                output_path=output_path,\n                filename=self.model_id,\n            )\n            logging.info(\n                f\"{self.model_id}: Your model's metadata was updated. Find it in {output_path}/{self.model_id}.json\"\n            )\n\n        self.metadata = metadata_final\n        self.validate_and_update_model_weights_path()\n\n        return self.metadata\n\n    def is_value_for_key_already_set(\n        self, key: str, metadata: dict, nested_key\n    ) -> bool:\n        \"\"\"Check if the value of a `key` in a `metadata` dictionary is already set and e.g. not an empty string, dict or list.\n\n        Parameters\n        ----------\n        key: str\n            The key in the currently traversed part of the model's metadata dictionary\n        metadata: dict\n            The currently traversed part of the model's metadata dictionary\n        nested_key: str\n            the `nested_key` indicates which subpart of the model's metadata we are currently traversing\n\n        Returns\n        -------\n        bool\n            Flag indicating whether a value exists for the `key` in the dict\n        \"\"\"\n\n        if (\n            metadata.get(key) is None\n            or metadata.get(key) == \"\"\n            or (isinstance(metadata.get(key), list) and not metadata.get(key))\n            or isinstance(metadata.get(key), dict)\n        ):\n            # Note: If metadata.get(key) is referencing a dict, we always want to go inside the dict and add values.\n            return False\n        else:\n            logging.debug(\n                f\"{self.model_id}: Key value pair ({key}:{metadata.get(key)}) already exists in metadata for key \"\n                f\"'{nested_key}'. Not prompting user to insert value for this key.\"\n            )\n            return True\n\n    def _recursively_fill_metadata(\n        self, metadata_template: dict = None, metadata: dict = {}, nested_key: str = \"\"\n    ) -> dict:\n        \"\"\"Filling a model metadata template with values retrieved via user input prompts and by traversing nested dicts and list recursively.\n\n        Parameters\n        ----------\n        metadata_template: dict\n            The template containing all keys expected in a model's metadata dictionary.\n        metadata: dict\n            The currently traversed part of the model's metadata dictionary\n        nested_key: str\n            the `nested_key` indicates which subpart of the model's metadata we are currently traversing\n\n        Returns\n        -------\n        dict\n            The final fully filled metadata dictionary.\n        \"\"\"\n\n        if metadata_template is None:\n            metadata_template = self.metadata_template\n        # Prompt user for optional metadata input\n        retrieved_nested_key = nested_key\n        for key in metadata_template:\n            # nested_key to know where we are inside the metadata dict.\n            nested_key = (\n                key if retrieved_nested_key == \"\" else f\"{retrieved_nested_key}.{key}\"\n            )\n            if not self.is_value_for_key_already_set(\n                key=key, metadata=metadata, nested_key=nested_key\n            ):\n                value_template = metadata_template.get(key)\n                if value_template is None:\n                    input_value = input(\n                        f\"{self.model_id}: Please enter value of type float or int for your model for key '{nested_key}': \"\n                    )\n                    try:\n                        value_assigned = float(input_value.replace(\",\", \".\"))\n                    except ValueError:\n                        value_assigned = (\n                            int(input_value) if input_value.isdigit() else None\n                        )\n                elif isinstance(value_template, list):\n                    input_value = input(\n                        f\"{self.model_id}: Please enter a comma-separated list of values for your model for key: '{nested_key}': \"\n                    )\n                    value_assigned = (\n                        [value.strip() for value in input_value.split(\",\")]\n                        if input_value != \"\"\n                        else []\n                    )\n                elif isinstance(value_template, str):\n                    value_assigned = str(\n                        input(\n                            f\"{self.model_id}: Please enter value of type string for your model for key '{nested_key}': \"\n                        )\n                    )\n                elif isinstance(value_template, dict):\n                    if len(value_template) == 0:\n                        # If dict is empty, no recursion. Instead, we ask the user directly for input.\n                        iterations = int(\n                            input(\n                                f\"{self.model_id}: How many key-value pairs do you want to nest below key '{nested_key}' \"\n                                f\"in your model's metadata. Type a number: \"\n                            )\n                            or \"0\"\n                        )\n                        nested_metadata: dict = {}\n                        for i in range(iterations):\n                            nested_key_input = str(\n                                input(f\"{self.model_id}: Enter key {i + 1}: \")\n                            )\n                            nested_value_input = input(\n                                f\"{self.model_id}: For key{i + 1}={nested_key_input}, enter value: \"\n                            )\n                            nested_metadata.update(\n                                {nested_key_input: nested_value_input}\n                            )\n                        value_assigned = nested_metadata\n                    else:\n                        # From metadata, get the nested dict below the key. If metadata has no nested dict, get the\n                        # template's nested dict instead, which is stored in value_template\n                        temp_metadata = (\n                            metadata.get(key)\n                            if metadata.get(key) is not None\n                            else value_template\n                        )\n                        # Filling nested dicts via recursion. value_assigned is of type dict in this case.\n                        value_assigned = self._recursively_fill_metadata(\n                            metadata_template=value_template,\n                            nested_key=nested_key,\n                            metadata=temp_metadata,\n                        )\n                logging.debug(\n                    f\"{self.model_id}: You provided this key-value pair: {key}={value_assigned}\"\n                )\n                metadata.update({key: value_assigned})\n        return metadata\n\n    def __repr__(self):\n        return f\"ModelContributor(model_id={self.model_id}, metadata={self.metadata})\"\n\n    def __len__(self):\n        raise NotImplementedError\n\n    def __getitem__(self, idx: int):\n        raise NotImplementedError\n"
  },
  {
    "path": "src/medigan/contribute_model/zenodo_model_uploader.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\"Zenodo Model uploader class that uploads models to medigan associated data storage on Zenodo. \"\"\"\n\nfrom __future__ import absolute_import\n\nimport json\nimport logging\nimport shutil\nfrom pathlib import Path\n\nimport requests\n\nfrom ..constants import (\n    CONFIG_FILE_KEY_DESCRIPTION,\n    CONFIG_FILE_KEY_SELECTION,\n    CONFIG_FILE_KEY_TAGS,\n    ZENODO_API_URL,\n    ZENODO_GENERIC_MODEL_DESCRIPTION,\n    ZENODO_HEADERS,\n    ZENODO_LINE_BREAK,\n)\nfrom .base_model_uploader import BaseModelUploader\n\n\nclass ZenodoModelUploader(BaseModelUploader):\n    \"\"\"`ZenodoModelUploader` class: Uploads a user's model via API to Zenodo, here it is permanently stored with DOI.\n\n    Parameters\n    ----------\n    model_id: str\n        The generative model's unique id\n    access_token: str\n        a personal access token in Zenodo linked to a user account for authentication\n\n    Attributes\n    ----------\n    model_id: str\n        The generative model's unique id\n    access_token: str\n        a personal access token in Zenodo linked to a user account for authentication\n    \"\"\"\n\n    def __init__(\n        self,\n        model_id,\n        access_token,\n    ):\n        self.model_id = model_id\n        self.params = {\"access_token\": access_token}\n\n    ############################ UPLOAD ############################\n    def create_upload_description(\n        self, metadata: dict, model_description: str = \"\"\n    ) -> str:\n        \"\"\"Create a string containing the textual description that will accompany the upload model files.\n\n        The string contains tags and a text retrieved from the description subsection of the model metadata.\n\n        Parameters\n        ----------\n        metadata: dict\n            The model's corresponding metadata\n        model_description: str\n            the model_description that will appear on the corresponding Zenodo model upload homepage\n\n        Returns\n        -------\n        str\n            Returns the textual description of the model upload\n        \"\"\"\n\n        try:\n            tags = f\"{ZENODO_LINE_BREAK} <p><strong>Tags:</strong></p> {metadata[self.model_id][CONFIG_FILE_KEY_SELECTION][CONFIG_FILE_KEY_TAGS]}\"\n        except:\n            tags = \"\"\n        try:\n            description_from_config = f\"<p><strong> Description from model config:</strong></p>: {json.dumps(metadata[self.model_id][CONFIG_FILE_KEY_DESCRIPTION])}\"\n        except:\n            description_from_config = \"\"\n\n        return f\"{model_description} <p><strong>Model ID:</strong></p> {self.model_id}. {ZENODO_LINE_BREAK} <p><strong>Uploaded via:</strong></p> API {tags} {ZENODO_LINE_BREAK} {ZENODO_GENERIC_MODEL_DESCRIPTION.replace('YOUR_MODEL_ID', self.model_id)} {description_from_config} {ZENODO_LINE_BREAK}\"\n\n    def create_upload_json_data(\n        self, creator_name: str, creator_affiliation: str, description: str = \"\"\n    ) -> dict:\n        \"\"\"Create some descriptive data in dict format to be uploaded and stored alongside the model files.\n\n        Parameters\n        ----------\n        creator_name: str\n            the creator name that will appear on the corresponding Zenodo model upload homepage\n        creator_affiliation: str\n            the creator affiliation that will appear on the corresponding Zenodo model upload homepage\n        description: str\n            the model_description that will appear on the corresponding Zenodo model upload homepage\n\n        Returns\n        -------\n        dict\n            Returns the descriptive data in dictionary structure describing the model upload.\n        \"\"\"\n\n        return {\n            \"metadata\": {\n                \"title\": f\"MEDIGAN MODEL UPLOAD: {self.model_id}\",\n                \"upload_type\": \"software\",\n                \"description\": description,\n                \"creators\": [\n                    {\n                        \"name\": f\"{creator_name}\",\n                        \"affiliation\": f\"{creator_affiliation}\",\n                    }\n                ],\n            }\n        }\n\n    def locate_or_create_model_zip_file(\n        self, package_path: str, package_name: str\n    ) -> (str, str):\n        \"\"\"If not possible to locate, create a zipped python package of the model.\n\n        Parameters\n        ----------\n        package_path: str\n            Path as string to the generative model's python package containing an `__init__.py` file\n        package_name: str\n            Name of the model's python package i.e. the name of the model's zip file and unzipped package folder\n\n        Returns\n        -------\n        tuple\n            Returns a tuple containing two strings: The `filename` and the `file_path` of and to the zipped python package\n        \"\"\"\n\n        # Check if zip file already exists\n        if not (Path(package_path).is_file() and package_path.endswith(\".zip\")):\n            # Create a zip archive containing the model package and store that zip file inside the\n            # folder of the model package\n            package_parent_path = str(Path(package_path).parent)\n            logging.info(\n                f\"Archiving the model package as zip archive: base_name={package_parent_path+ '/' + package_name}, root_dir={package_path + '/'} \"\n            )\n            filename = shutil.make_archive(\n                base_name=package_parent_path + \"/\" + package_name,\n                format=\"zip\",\n                root_dir=package_path,\n            )\n            file_path = filename\n            filename = Path(file_path).name\n        else:\n            filename = Path(package_path).name\n            file_path = package_path\n        logging.info(\n            f\"Model was successfully archived as zip archive: filename={filename}, file_path={file_path} \"\n        )\n        return filename, file_path\n\n    def empty_upload(self) -> dict:\n        \"\"\"Upload an empty placeholder entry to Zenodo as is required to retrieve a `deposition_id` and `bucket_url`.\n\n        deposition_id and bucket_url aare needed for file upload and publishing in the subsequent upload steps.\n\n        Returns\n        -------\n        dict\n            Returns the response retrieved via the Zenodo API\n        \"\"\"\n\n        r = requests.post(\n            ZENODO_API_URL,\n            params=self.params,\n            json={},\n            headers=ZENODO_HEADERS,\n        )\n        if not r.status_code == 201:\n            raise Exception(\n                f\"{self.model_id}: Error ({r.status_code}!=201) during Zenodo ('{ZENODO_API_URL}') upload (step 1: creating empty upload template): {r.json()}.\"\n            )\n        return r\n\n    def upload(self, file_path: str, filename: str, bucket_url: str) -> dict:\n        \"\"\"Upload a file to Zenodo entry of the uploaded model files.\n\n        Parameters\n        ----------\n        file_path: str\n            The path of the file that is uploaded to Zenodo\n        filename: str\n            The name of the file that is uploaded to Zenodo\n        bucket_url: str\n            The bucket url used in the PUT request to upload the data file.\n\n        Returns\n        -------\n        dict\n            Returns the response retrieved via the Zenodo API\n        \"\"\"\n        with open(file_path, \"rb\") as fp:\n            r = requests.put(\n                \"%s/%s\" % (bucket_url, filename),\n                data=fp,\n                params=self.params,\n            )\n\n        if not r.status_code == 200 and not r.status_code == 201:\n            raise Exception(\n                f\"{self.model_id}: Error ({r.status_code}!= any of (200, 201) ) during Zenodo ('{bucket_url}') upload (step 2: uploading model as zip file): {r.json()}\"\n            )\n        return r\n\n    def upload_descriptive_data(self, deposition_id: str, data: dict) -> dict:\n        \"\"\"Upload textual descriptive data to be associated and added to the Zenodo entry of the uploaded model files.\n\n        Parameters\n        ----------\n        deposition_id: str\n            The deposition id assigned by Zenodo to the uploaded model file\n        data: dict\n            The descriptive information that will to be uploaded to Zenodo and associated with the desposition_id\n\n        Returns\n        -------\n        dict\n            Returns the response retrieved via the Zenodo API\n        \"\"\"\n        deposition_url = f\"{ZENODO_API_URL}/{deposition_id}\"\n        r = requests.put(\n            deposition_url,\n            params=self.params,\n            data=json.dumps(data),\n            headers=ZENODO_HEADERS,\n        )\n        if not r.status_code == 200 and not r.status_code == 201:\n            raise Exception(\n                f\"{self.model_id}: Error ({r.status_code}!= any of (200, 201) ) during Zenodo ('{deposition_url}') upload (step 3: updating metadata): {r.json()}\"\n            )\n        return r\n\n    def publish(self, deposition_id: str) -> dict:\n        \"\"\"Publish a zenodo upload.\n\n        This makes the upload official, as it will then be publicly accessible and persistently stored on Zenodo with associated DOI.\n\n        Parameters\n        ----------\n        deposition_id: str\n            The deposition id assigned by Zenodo to the uploaded model file\n\n        Returns\n        -------\n        dict\n            Returns the response retrieved via the Zenodo API\n        \"\"\"\n\n        # Get explicit user approval to publish on Zenodo. Published files cannot be deleted.\n        is_user_sure = str(\n            input(\n                f\"You are about to publish model {self.model_id} with Zenodo-ID {deposition_id} permanently on {ZENODO_API_URL.replace('/api/deposit/depositions','')}. To proceed, type 'Yes': \"\n            )\n        )\n        publish_url = f\"{ZENODO_API_URL}/{deposition_id}/actions/publish\"\n        if is_user_sure == \"Yes\":\n            r = requests.post(\n                publish_url,\n                params=self.params,\n            )\n        else:\n            raise Exception(\n                f\"{self.model_id}: Error during Zenodo ('{publish_url}') upload (step 4: publishing uploaded model) due to user opt-out: You typed '{is_user_sure}' instead of 'Yes'. Model was not published. Try again. Your Zenodo deposition ID (if retrieved): '{deposition_id}'.\"\n            )\n        if not r.status_code == 202:\n            raise Exception(\n                f\"{self.model_id}: Error ({r.status_code}!=202) during Zenodo ('{publish_url}') upload (step 4: publishing uploaded model): {r.json()}\"\n            )\n        logging.info(\n            f\"{self.model_id}: Successfully pushed model to Zenodo with DOI '{r.json()['doi']}': '{r.json()['links']['record_html']}\"\n        )\n        logging.debug(\n            f\"{self.model_id}: Full Zenodo API response after successful publishing of model: {r.json()}\"\n        )\n        return r\n\n    def push(\n        self,\n        metadata: dict,\n        package_path: str,\n        package_name: str,\n        creator_name: str,\n        creator_affiliation: str,\n        model_description: str = \"\",\n    ):\n        \"\"\"Upload the model files as zip archive to a public Zenodo repository where the model will be persistently stored.\n\n        Get your Zenodo access token here: https://zenodo.org/account/settings/applications/tokens/new/ (Enable scopes `deposit:actions` and `deposit:write`)\n\n        Parameters\n        ----------\n        metadata: dict\n            The model's corresponding metadata\n        package_path: dict\n            The path to the packaged model files\n        package_name: dict\n            The name of the packaged model files\n        creator_name: str\n            the creator name that will appear on the corresponding Zenodo model upload homepage\n        creator_affiliation: str\n            the creator affiliation that will appear on the corresponding Zenodo model upload homepage\n        model_description: list\n            the model_description that will appear on the corresponding Zenodo model upload homepage\n\n        Returns\n        -------\n        str\n            Returns the url pointing to the corresponding Zenodo model upload homepage\n        \"\"\"\n\n        # Check if zip file exists, else create new one for upload.\n        filename, file_path = self.locate_or_create_model_zip_file(\n            package_path=package_path, package_name=package_name\n        )\n\n        # create empty upload to Zenodo to get deposition_id and bucket_url\n        response = self.empty_upload()\n        logging.debug(f\"API Response after creating empty upload template: {response}\")\n\n        # Get the deposition id from the response\n        deposition_id = response.json()[\"id\"]\n\n        # Using bucket as defined by Zenodo API for zip file model upload\n        bucket_url = response.json()[\"links\"][\"bucket\"]\n\n        logging.info(\n            f\"Starting Zenodo upload of model with deposition_id {deposition_id} to {bucket_url}\"\n        )\n        response = self.upload(\n            file_path=file_path,\n            filename=filename,\n            bucket_url=bucket_url,\n        )\n        logging.debug(\n            f\"API Response after uploading model to '{bucket_url}': {response}\"\n        )\n\n        # get the model description i.e. model type, metadata info, etc.\n        description = self.create_upload_description(\n            metadata=metadata, model_description=model_description\n        )\n\n        # get the data that includes description, but also creator information\n        data = self.create_upload_json_data(\n            description=description,\n            creator_name=creator_name,\n            creator_affiliation=creator_affiliation,\n        )\n\n        # upload the model zip file and its descriptive data\n        response = self.upload_descriptive_data(deposition_id=deposition_id, data=data)\n        logging.debug(\n            f\"API Response after uploading descriptive model data: {response}\"\n        )\n\n        # publish to Zenodo. Model will get DOI after this step and become part of Zenodo's permanent record.\n        response = self.publish(deposition_id=deposition_id)\n        logging.debug(\n            f\"API Response after publishing the deposition {deposition_id} on Zenodo: {response}\"\n        )\n        return response.json()[\"links\"][\"record_html\"]  # zenodo_record_url\n\n    def __repr__(self):\n        return f\"ZenodoModelUploader(model_id={self.model_id}, zenodo_url={ZENODO_API_URL})\"\n\n    def __len__(self):\n        raise NotImplementedError\n\n    def __getitem__(self, idx: int):\n        raise NotImplementedError\n"
  },
  {
    "path": "src/medigan/exceptions.py",
    "content": "# -*- coding: utf-8 -*-\n#! /usr/bin/env python\n\"\"\"Custom exceptions to handle module specific error and facilitate bug fixes and debugging.\"\"\"\n\n# TODO Add custom exceptions for improved exception handling.\n# See requests.exceptions for reference.\n"
  },
  {
    "path": "src/medigan/execute_model/__init__.py",
    "content": ""
  },
  {
    "path": "src/medigan/execute_model/install_model_dependencies.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" Functionality for automated installation of a model's python package dependencies. \"\"\"\n\nimport argparse\nimport subprocess\nimport sys\n\ntry:\n    # if called as script (__main__) or from inside medigan\n    from ..config_manager import ConfigManager\n    from ..constants import CONFIG_FILE_KEY_DEPENDENCIES, CONFIG_FILE_KEY_EXECUTION\nexcept:\n    # if called from outside medigan\n    from medigan.config_manager import ConfigManager\n    from medigan.constants import (\n        CONFIG_FILE_KEY_DEPENDENCIES,\n        CONFIG_FILE_KEY_EXECUTION,\n    )\n\n\ndef parse_args() -> argparse.Namespace:\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        \"--model_id\",\n        type=str,\n        default=None,\n        nargs=\"+\",\n        help=\"Model ids to install dependencies for\",\n    )\n    args = parser.parse_args()\n    return args\n\n\ndef install_model(\n    model_id: str, config_manager: ConfigManager = None, execution_config: dict = None\n):\n    \"\"\"installing the dependencies required for this model as stated in config\"\"\"\n\n    if execution_config is None:\n        if config_manager is None:\n            config_manager = ConfigManager()\n        config = config_manager.get_config_by_id(model_id)\n        execution_config = config[CONFIG_FILE_KEY_EXECUTION]\n    dependencies = execution_config[CONFIG_FILE_KEY_DEPENDENCIES]\n    for package in dependencies:\n        subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", package])\n\n\nif __name__ == \"__main__\":\n    \"\"\"\n    This script is used to install dependencies for models.\n    If no model_id is provided, all models from the config file are installed.\n    \"\"\"\n    args = parse_args()\n\n    config_manager = ConfigManager()\n\n    if args.model_id:\n        for model_id in args.model_id:\n            install_model(model_id)\n    else:\n        for model_id in config_manager.config_dict.keys():\n            install_model(model_id)\n"
  },
  {
    "path": "src/medigan/execute_model/model_executor.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\"Model executor class that downloads models, loads them as python packages, and runs their generate functions. \"\"\"\n\n# Import python native libs\nfrom __future__ import absolute_import\n\nimport importlib\nimport logging\nimport os\nimport time\n\n# Import pypi libs\nfrom pathlib import Path\n\nimport pkg_resources\nfrom tqdm import tqdm\n\n# Import library internal modules\nfrom ..constants import (\n    CONFIG_FILE_KEY_DEPENDENCIES,\n    CONFIG_FILE_KEY_GENERATE,\n    CONFIG_FILE_KEY_GENERATE_ARGS,\n    CONFIG_FILE_KEY_GENERATE_ARGS_BASE,\n    CONFIG_FILE_KEY_GENERATE_ARGS_CUSTOM,\n    CONFIG_FILE_KEY_GENERATE_ARGS_INPUT_LATENT_VECTOR_SIZE,\n    CONFIG_FILE_KEY_GENERATE_ARGS_MODEL_FILE,\n    CONFIG_FILE_KEY_GENERATE_ARGS_NUM_SAMPLES,\n    CONFIG_FILE_KEY_GENERATE_ARGS_OUTPUT_PATH,\n    CONFIG_FILE_KEY_GENERATE_ARGS_SAVE_IMAGES,\n    CONFIG_FILE_KEY_GENERATE_NAME,\n    CONFIG_FILE_KEY_IMAGE_SIZE,\n    CONFIG_FILE_KEY_MODEL_EXTENSION,\n    CONFIG_FILE_KEY_MODEL_NAME,\n    CONFIG_FILE_KEY_PACKAGE_LINK,\n    CONFIG_FILE_KEY_PACKAGE_NAME,\n    DEFAULT_OUTPUT_FOLDER,\n    MODEL_FOLDER,\n    PACKAGE_EXTENSION,\n)\nfrom ..utils import Utils\nfrom .install_model_dependencies import install_model\n\n\nclass ModelExecutor:\n    \"\"\"`ModelExecutor` class: Find config links to download models, init models as python packages, run generate methods.\n\n    Parameters\n    ----------\n    model_id: str\n        The generative model's unique id\n    execution_config: dict\n        The part of the config below the 'execution' key\n    download_package: bool\n        Flag indicating, if True, that the model's package should be downloaded instead of using an existing one that\n        was downloaded previously\n    install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n\n\n    Attributes\n    ----------\n    model_id: str\n        The generative model's unique id\n    execution_config: dict\n        The part of the config below the 'execution' key\n    download_package: bool\n        Flag indicating, if True, that the model's package should be downloaded instead of using an existing one that\n        was downloaded previously\n    image_size: int\n        Pixel dimension of the generated samples, where images are assumed to have the same width and height\n    dependencies: list\n        List of the dependencies of a models python package.\n    model_name: str\n        Name of the generative model\n    model_extension: str\n        File extension of the generative model's weights file.\n    package_name: str\n        Name of the model's python package i.e. the name of the model's zip file and unzipped package folder\n    package_link: str\n        The link to the zipped model package. Note: Convention is to host models on Zenodo (reason: static doi content)\n    generate_method_name: str\n        The name of the model's generate method inside the model package. This method is called to generate samples.\n    generate_method_args: dict\n        The args of the model's generate method inside the model package\n    serialised_model_file_path: str\n        Path as string to the generative model's weights file\n    package_path: str\n        Path as string to the generative model's python package containing an `__init__.py` file\n    deserialized_model_as_lib\n        The generative model's package imported as python library. Generate method inside this library can be called.\n    \"\"\"\n\n    def __init__(\n        self,\n        model_id: str,\n        execution_config: dict,\n        download_package: bool = True,\n        install_dependencies: bool = False,\n    ):\n        self.model_id = model_id\n        self.execution_config = execution_config\n        self.download_package = download_package\n        self.install_dependencies = install_dependencies\n        self.image_size = None\n        self.dependencies = None\n        self.model_name = None\n        self.model_extension = None\n        self.package_name = None\n        self.package_link = None\n        self.generate_method_name = None\n        self.generate_method_args = None\n        self.generate_method_input_latent_vector_size = None\n        self.serialised_model_file_path = None\n        self.package_path = None\n        self.deserialized_model_as_lib = None\n        self._setup_model_package()\n\n    def _setup_model_package(self):\n        \"\"\"Use specific keys to retrieve needed model config values and load and initialize the model as package.\"\"\"\n\n        self.image_size = self.execution_config[CONFIG_FILE_KEY_IMAGE_SIZE]\n        self.dependencies = self.execution_config[CONFIG_FILE_KEY_DEPENDENCIES]\n        self.model_name = self.execution_config[CONFIG_FILE_KEY_MODEL_NAME]\n        self.model_extension = self.execution_config[CONFIG_FILE_KEY_MODEL_EXTENSION]\n        self.package_name = self.execution_config[CONFIG_FILE_KEY_PACKAGE_NAME]\n        self.package_link = self.execution_config[CONFIG_FILE_KEY_PACKAGE_LINK]\n        self.generate_method_name = self.execution_config[CONFIG_FILE_KEY_GENERATE][\n            CONFIG_FILE_KEY_GENERATE_NAME\n        ]\n        self.generate_method_args = self.execution_config[CONFIG_FILE_KEY_GENERATE][\n            CONFIG_FILE_KEY_GENERATE_ARGS\n        ]\n        if (\n            CONFIG_FILE_KEY_GENERATE_ARGS_INPUT_LATENT_VECTOR_SIZE\n            in self.execution_config[CONFIG_FILE_KEY_GENERATE]\n        ):\n            self.generate_method_input_latent_vector_size = self.execution_config[\n                CONFIG_FILE_KEY_GENERATE\n            ][CONFIG_FILE_KEY_GENERATE_ARGS_INPUT_LATENT_VECTOR_SIZE]\n\n        self._check_package_resources()\n        if not self.is_model_already_unpacked():\n            self._get_and_store_package()\n        self._import_package_as_lib()\n\n    def _check_package_resources(self):\n        \"\"\"Check if the dependencies inside the generative model's package are installed in the current setup.\"\"\"\n\n        logging.debug(\n            f\"{self.model_id}: Now checking availability of dependencies of model: {self.dependencies}\"\n        )\n        try:\n            pkg_resources.require(self.dependencies)\n            logging.debug(\n                f\"{self.model_id}: All necessary dependencies for model are available: {self.dependencies}\"\n            )\n        except Exception as e:\n            if self.install_dependencies:\n                logging.info(\n                    f\"{self.model_id}: Now installing dependencies using pip for model {self.dependencies}. This may take a few minutes.\"\n                )\n                install_model(\n                    model_id=self.model_id, execution_config=self.execution_config\n                )\n            else:\n                raise Exception(\n                    f\"{self.model_id}: Some of the necessary dependencies ({self.dependencies}) for this model \"\n                    f\"are missing. Either set install_dependencies=True or manually run 'python src/medigan/install_model_dependencies.py --model_id {self.model_id}' to install them. Error: {e}\"\n                )\n\n    def _get_and_store_package(self):\n        \"\"\"Load and store the generative model's python package using the link from the model's `execution_config`.\"\"\"\n\n        if self.package_path is None:\n            assert Utils.mkdirs(path_as_string=f\"{MODEL_FOLDER}/{self.model_id}\"), (\n                f\"{self.model_id}: The model folder was not found nor created \"\n                f\"in {MODEL_FOLDER}/{self.model_id}.\"\n            )\n            package_path = Path(\n                f\"{MODEL_FOLDER}/{self.model_id}/{self.package_name}{PACKAGE_EXTENSION}\"\n            )\n            try:\n                if not Utils.is_file_located_or_downloaded(\n                    path_as_string=package_path,\n                    download_if_not_found=True,\n                    download_link=self.package_link,\n                ):\n                    error_string = (\n                        f\"{self.model_id}: The package archive ({self.package_name}{PACKAGE_EXTENSION}) \"\n                        f\"was not found in {package_path} nor downloaded from {self.package_link}.\"\n                    )\n                    raise FileNotFoundError(error_string)\n            except Exception as e:\n                raise e\n            self.package_path = package_path\n        logging.info(\n            f\"{self.model_id}: Model package should now be available in: {self.package_path}.\"\n        )\n\n    def is_model_already_unpacked(self) -> bool:\n        \"\"\"Check if a valid path to the model files exists and, if so, set the `package_path`\"\"\"\n\n        path_option_1 = Path(\n            f\"{MODEL_FOLDER}/{self.model_id}/{self.package_name}/{self.model_name}{self.model_extension}\"\n        )\n\n        path_option_2 = Path(\n            f\"{MODEL_FOLDER}/{self.model_id}/{self.model_name}{self.model_extension}\"\n        )\n\n        if path_option_1.is_file():\n            self.package_path = path_option_1\n            return True\n\n        if path_option_2.is_file():\n            self.package_path = path_option_2\n            return True\n\n        return False\n\n    def _import_package_as_lib(self):\n        \"\"\"Unzip and import the generative model's python package using importlib.\"\"\"\n\n        logging.debug(\n            f\"{self.model_id}: Now importing model package ({self.package_name}) as lib using \"\n            f\"importlib from {self.package_path}.\"\n        )\n        is_model_already_unpacked = self.is_model_already_unpacked()\n        # if is_model_already_unpacked == True, then the package was already unzipped previously.\n\n        if (\n            self.package_path.is_file()\n            and PACKAGE_EXTENSION == \".zip\"\n            and not is_model_already_unpacked\n        ):\n            # Unzip the model package in {MODEL_FOLDER}/{model_id}/{MODEL_PACKAGE}{PACKAGE_EXTENSION}\n            Utils.unzip_archive(\n                source_path=self.package_path,\n                target_path=f\"{MODEL_FOLDER}/{self.model_id}\",\n            )\n        else:\n            logging.debug(\n                f\"{self.model_id}: Either no file found (== {self.package_path.is_file()}) or package \"\n                f\"already unarchived (=={is_model_already_unpacked}) in {self.package_path}. \"\n                f\"No action was taken.\"\n            )\n        try:\n            # Installing generative model as python library\n            self.deserialized_model_as_lib = importlib.import_module(\n                name=f\"{MODEL_FOLDER}.{self.model_id}.{self.package_name}\"\n            )\n            if not hasattr(\n                self.deserialized_model_as_lib, f\"{self.generate_method_name}\"\n            ):\n                # if generate method is not in lib path, generating samples will not work. Next: Check fallback folder.\n                raise ModuleNotFoundError\n            self.serialised_model_file_path = f\"{MODEL_FOLDER}/{self.model_id}/{self.package_name}/{self.model_name}{self.model_extension}\"\n        except ModuleNotFoundError:\n            try:\n                # Fallback: The zip's content might have been unzipped in the model_id folder without generating the package_name subfolder.\n                self.deserialized_model_as_lib = importlib.import_module(\n                    name=f\"{MODEL_FOLDER}.{self.model_id}\"\n                )\n                if not hasattr(\n                    self.deserialized_model_as_lib, f\"{self.generate_method_name}\"\n                ):\n                    # if generate method is not in lib path, generating samples will not work. Next: Check fallback folder.\n                    raise AttributeError(\n                        f\"Module '{MODEL_FOLDER}.{self.model_id}' has no attribute \"\n                        f\"'{self.generate_method_name}' (generate method). We also tried module \"\n                        f\"'{MODEL_FOLDER}.{self.model_id}.{self.package_name}'. Please check if \"\n                        f\"generate_method_name and package_name are correct for this model in its \"\n                        f\"global.json entry.\"\n                    )\n                self.serialised_model_file_path = f\"{MODEL_FOLDER}/{self.model_id}/{self.model_name}{self.model_extension}\"\n            except Exception as e:\n                logging.error(\n                    f\"{self.model_id}: Error occurred while trying to import \"\n                    f\"'{MODEL_FOLDER}.{self.model_id}.{self.package_name}'.\"\n                    f\"Fallback import of '{MODEL_FOLDER}.{self.model_id}' also failed. \"\n                    f\"Please make sure the module '{MODEL_FOLDER}' is not imported from elsewhere in your syspath: {e}\"\n                )\n                raise e\n\n    def generate(\n        self,\n        num_samples: int = 20,\n        output_path: str = None,\n        save_images: bool = True,\n        is_gen_function_returned: bool = False,\n        batch_size: int = 32,\n        **kwargs,\n    ):\n        \"\"\"Generate samples using the generative model or return the model's generate function.\n\n        The name amd additional parameters of the generate function of the respective generative model are retrieved\n        from the model's `execution_config`.\n\n        Parameters\n        ----------\n        num_samples: int\n            the number of samples that will be generated\n        output_path: str\n            the path as str to the output folder where the generated samples will be stored\n        save_images: bool\n            flag indicating whether generated samples are returned (i.e. as list of numpy arrays) or rather stored in file system (i.e in `output_path`)\n        is_gen_function_returned: bool\n            flag indicating whether, instead of generating samples, the sample generation function will be returned\n        batch_size: int\n            the batch size for the sample generation function\n        **kwargs\n            arbitrary number of keyword arguments passed to the model's sample generation function\n\n        Returns\n        -------\n        list\n            Returns images as list of numpy arrays if `save_images` is False. However, if `is_gen_function_returned` is True, it returns the internal generate function of the model.\n\n        Raises\n        ------\n        Exception\n            If the generate method of the model does not exist, cannot be called, or is called with missing params, or\n            if the sample generation inside the model package returns an exception.\n        \"\"\"\n\n        if output_path is None:\n            output_path = f\"{DEFAULT_OUTPUT_FOLDER}/{self.model_id}/{time.time()}/\"\n        assert Utils.mkdirs(\n            path_as_string=output_path\n        ), f\"{self.model_id}: The output folder was not found nor created in {output_path}.\"\n        try:\n            generate_method = getattr(\n                self.deserialized_model_as_lib, f\"{self.generate_method_name}\"\n            )\n            prepared_kwargs = self._prepare_generate_method_args(\n                model_file=self.serialised_model_file_path,\n                num_samples=num_samples,\n                output_path=output_path,\n                save_images=save_images,\n                **kwargs,\n            )\n            logging.debug(f\"The generate function's parameters are: {prepared_kwargs}\")\n            if is_gen_function_returned:\n\n                def gen(**some_other_kwargs):\n                    logging.debug(\n                        f\"Generate method called with the following params. (i) default: {prepared_kwargs}, \"\n                        f\"(ii) custom: {some_other_kwargs}\"\n                    )\n                    prepared_kwargs.update(some_other_kwargs)\n                    return generate_method(**prepared_kwargs)\n\n                return gen\n            elif save_images:\n                sample_index = 1\n                prepared_kwargs.update({\"num_samples\": batch_size})\n                for batch_num in tqdm(range(0, num_samples // batch_size + 1)):\n                    if batch_num == num_samples // batch_size:\n                        batch_size = num_samples % batch_size\n                        prepared_kwargs.update({\"num_samples\": batch_size})\n\n                    batch_path = (\n                        os.path.join(output_path, \"batch_\" + str(batch_num)) + \"/\"\n                    )\n\n                    # Generate the path in case it is not yet available.\n                    assert Utils.mkdirs(\n                        path_as_string=batch_path\n                    ), f\"{self.model_id}: The batch path was not found nor created in {batch_path}.\"\n\n                    prepared_kwargs.update({\"output_path\": batch_path})\n\n                    generate_method(**prepared_kwargs)\n\n                    for filename in os.listdir(batch_path):\n                        os.rename(\n                            os.path.join(batch_path, filename),\n                            os.path.join(\n                                output_path, \"batch_\" + str(batch_num) + \"_\" + filename\n                            ),\n                        )\n                        sample_index += 1\n\n                    os.rmdir(batch_path)\n            else:\n                return generate_method(**prepared_kwargs)\n\n        except Exception as e:\n            logging.error(\n                f\"{self.model_id}: Error while trying to generate images with model \"\n                f\"{self.serialised_model_file_path}: {e}\"\n            )\n            raise e\n\n    def _prepare_generate_method_args(\n        self,\n        model_file: str,\n        num_samples: int,\n        output_path: str,\n        save_images: bool,\n        **kwargs,\n    ):\n        \"\"\"Prepare the keyword arguments that will be passed to the models generate function.\n\n        Prepares the keyword arguments that need to be passed to the generative model's generate function to generate\n        samples. This contains the steps:\n\n            - Update keyword args dict with default values for all params from model config\n\n            - Update keyword args dict with the `**args` provided by user thus overwriting the previously set default\n                values for which user has provided key-value pairs.\n\n            - Checking if all mandatory 'base' values are set in model config.\n\n            - Update keyword args dict with 'base' key-value pairs, which i.e. are the param values for `model_file`,\n            `num_samples` and `output_path`, thus overwriting any previously set value for these keys.\n\n            - Returning the updated and prepared keyword args dict\n\n        Parameters\n        ----------\n        model_file : str\n            the path to the serialized weights of the generative model.\n        num_samples: int\n            the number of samples that will be generated\n        output_path: str\n            the path as str to the output folder where the generated samples will be stored\n        **kwargs\n            arbitrary number of keyword arguments passed to the model's sample generation function\n\n        Returns\n        -------\n        dict\n            kwargs as dictionary containing both user input params (prioritized) and config input params of the model\n        \"\"\"\n\n        prepared_kwargs: dict = {}\n        # get keys of mandatory custom dictionary input args and assign the default value from config to values of keys\n        prepared_kwargs.update(\n            self.generate_method_args[CONFIG_FILE_KEY_GENERATE_ARGS_CUSTOM]\n        )\n\n        # update: If one of these keys was provided in **kwargs, then change default value to value provided in **kwargs\n        prepared_kwargs.update(kwargs)\n\n        try:\n            # validating that these specific keys are available in the config. also retrieving default values\n            base_config_list = [\n                self.generate_method_args[CONFIG_FILE_KEY_GENERATE_ARGS_BASE][0],\n                self.generate_method_args[CONFIG_FILE_KEY_GENERATE_ARGS_BASE][1],\n                self.generate_method_args[CONFIG_FILE_KEY_GENERATE_ARGS_BASE][2],\n                self.generate_method_args[CONFIG_FILE_KEY_GENERATE_ARGS_BASE][3],\n            ]\n            if not all(\n                x in base_config_list\n                for x in [\n                    CONFIG_FILE_KEY_GENERATE_ARGS_MODEL_FILE,\n                    CONFIG_FILE_KEY_GENERATE_ARGS_NUM_SAMPLES,\n                    CONFIG_FILE_KEY_GENERATE_ARGS_OUTPUT_PATH,\n                    CONFIG_FILE_KEY_GENERATE_ARGS_SAVE_IMAGES,\n                ]\n            ):\n                raise KeyError\n        except KeyError as e:\n            logging.warning(\n                f\"{self.model_id}: Warning: In this model's generate args ({self.generate_method_args}), some \"\n                f\"required generate method keys ({CONFIG_FILE_KEY_GENERATE_ARGS_MODEL_FILE}, \"\n                f\"{CONFIG_FILE_KEY_GENERATE_ARGS_NUM_SAMPLES}, {CONFIG_FILE_KEY_GENERATE_ARGS_OUTPUT_PATH}, \"\n                f\"{CONFIG_FILE_KEY_GENERATE_ARGS_SAVE_IMAGES}) are missing: {e}. A value for this key will be \"\n                f\"provided nevertheless when calling the model's generate method ({self.generate_method_name})'. \"\n                f\"This could hence cause an error.\"\n            )\n        # Adding the always necessary base parameters to kwargs. They are updated if erroneously\n        # introduced via the user-provided kwargs.\n        prepared_kwargs.update(\n            {\n                CONFIG_FILE_KEY_GENERATE_ARGS_MODEL_FILE: model_file,\n                CONFIG_FILE_KEY_GENERATE_ARGS_NUM_SAMPLES: num_samples,\n                CONFIG_FILE_KEY_GENERATE_ARGS_OUTPUT_PATH: output_path,\n                CONFIG_FILE_KEY_GENERATE_ARGS_SAVE_IMAGES: save_images,\n            }\n        )\n        return prepared_kwargs\n\n    def __repr__(self):\n        return (\n            f\"ModelExecutor(model_id={self.model_id}, name={self.model_name}, package={self.package_name}, \"\n            f\"image_size={self.image_size}, dependencies={self.dependencies}, link={self.package_link}, \"\n            f\"path={self.serialised_model_file_path}, generate_method={self.generate_method_name}, \"\n            f\"generate_method_args={self.generate_method_args})\"\n        )\n\n    def __len__(self):\n        raise NotImplementedError\n\n    def __getitem__(self, idx: int):\n        raise NotImplementedError\n"
  },
  {
    "path": "src/medigan/execute_model/synthetic_dataset.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" `SyntheticDataset` allows to return a generative model as torch dataset. \"\"\"\n\nfrom torch.utils.data import Dataset\n\n\nclass SyntheticDataset(Dataset):\n    \"\"\"A synthetic dataset containing data generated by a model of medigan\n\n    Parameters\n    ----------\n    samples: list\n        List of data points in the dataset e.g. generated images as numpy array.\n    masks: list\n        List of segmentation masks, if applicable,  pertaining to the `samples` items\n    other_imaging_output: list\n        List of other imaging output produced by the generative model (e.g. specific types of other masks/modalities)\n    labels: list\n        list of labels, if applicable, pertaining to the `samples` items\n    transform:\n        torch compose transform functions that are applied to the torch dataset.\n\n    Attributes\n    ----------\n    samples: list\n        List of data points in the dataset e.g. generated images as numpy array.\n    masks: list\n        List of segmentation masks, if applicable,  pertaining to the `samples` items\n    other_imaging_output: list\n        List of other imaging output produced by the generative model (e.g. specific types of other masks/modalities)\n    labels: list\n        list of labels, if applicable, pertaining to the `samples` items\n    transform:\n        torch compose transform functions that are applied to the torch dataset.\n    \"\"\"\n\n    def __init__(\n        self,\n        samples,\n        masks=None,\n        other_imaging_output=None,\n        labels=None,\n        transform=None,\n    ):\n        self.samples = samples\n        self.masks = masks\n        self.other_imaging_output = other_imaging_output\n        self.labels = labels\n        self.transform = transform\n\n    def __getitem__(self, index):\n        x = self.samples[index]\n        y = self.labels[index] if self.labels is not None else None\n        mask = self.masks[index] if self.masks is not None else None\n        other_imaging_output = (\n            self.other_imaging_output[index]\n            if self.other_imaging_output is not None\n            else None\n        )\n\n        if self.transform:\n            if mask is not None:\n                if other_imaging_output is not None:\n                    x, mask, other_imaging_output = self.transform(\n                        x, mask, other_imaging_output\n                    )\n                # transform needs to be applied to both mask and image.\n                x, mask = self.transform(x, mask)\n            elif other_imaging_output is not None:\n                x, other_imaging_output = self.transform(x, other_imaging_output)\n            else:\n                x = self.transform(x)\n        item = {\"sample\": x}  # extendable dictionary\n        if y is not None:\n            item[\"label\"] = y\n        if mask is not None:\n            item[\"mask\"] = mask\n        if other_imaging_output is not None:\n            item[\"other_imaging_output\"] = other_imaging_output\n        return item\n\n    def __len__(self):\n        return len(self.samples)\n"
  },
  {
    "path": "src/medigan/generators.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" Base class providing user-library interaction methods for config management, and model selection and execution. \"\"\"\n\n# Import python native libs\nfrom __future__ import absolute_import\n\nimport logging\n\nfrom torch.utils.data import DataLoader, Dataset\n\n# Import library internal modules\nfrom .config_manager import ConfigManager\nfrom .constants import CONFIG_FILE_KEY_EXECUTION, MODEL_ID\nfrom .contribute_model.model_contributor import ModelContributor\nfrom .execute_model.model_executor import ModelExecutor\nfrom .execute_model.synthetic_dataset import SyntheticDataset\nfrom .model_visualizer import ModelVisualizer\nfrom .select_model.model_selector import ModelSelector\nfrom .utils import Utils\n\n# Import pypi libs\n\n\nclass Generators:\n    \"\"\"`Generators` class: Contains medigan's public methods to facilitate users' automated sample generation workflows.\n\n    Parameters\n    ----------\n    config_manager: ConfigManager\n        Provides the config dictionary, based on which `model_ids` are retrieved and models are selected and executed\n    model_selector: ModelSelector\n        Provides model comparison, search, and selection based on keys/values in the selection part of the config dict\n    model_executors: list\n        List of initialized `ModelExecutor` instances that handle model package download, init, and sample generation\n    initialize_all_models: bool\n        Flag indicating, if True, that one `ModelExecutor` for each `model_id` in the config dict should be\n        initialized triggered by creation of `Generators` class instance. Note that, if False, the `Generators` class\n        will only initialize a `ModelExecutor` on the fly when need be i.e. when the generate method for the respective\n        model is called.\n\n    Attributes\n    ----------\n    config_manager: ConfigManager\n        Provides the config dictionary, based on which model_ids are retrieved and models are selected and executed\n    model_selector: ModelSelector\n        Provides model comparison, search, and selection based on keys/values in the selection part of the config dict\n    model_executors: list\n        List of initialized `ModelExecutor` instances that handle model package download, init, and sample generation\n    \"\"\"\n\n    def __init__(\n        self,\n        config_manager: ConfigManager = None,\n        model_selector: ModelSelector = None,\n        model_executors: list = None,\n        model_contributors: list = None,\n        initialize_all_models: bool = False,\n    ):\n        if config_manager is None:\n            self.config_manager = ConfigManager()\n            logging.debug(f\"Initialized ConfigManager instance: {self.config_manager}\")\n        else:\n            self.config_manager = config_manager\n\n        if model_selector is None:\n            self.model_selector = ModelSelector(config_manager=self.config_manager)\n            logging.debug(f\"Initialized ModelSelector instance: {self.model_selector}\")\n        else:\n            self.model_selector = model_selector\n\n        if model_executors is None:\n            self.model_executors = []\n        else:\n            self.model_executors = model_executors\n\n        if model_contributors is None:\n            self.model_contributors = []\n        else:\n            self.model_contributors = model_contributors\n\n        if initialize_all_models:\n            self.add_all_model_executors()\n\n    ############################ CONFIG MANAGER METHODS ############################\n\n    def get_config_by_id(self, model_id: str, config_key: str = None) -> dict:\n        \"\"\"Get and return the part of the config below a `config_key` for a specific `model_id`.\n\n        The config_key parameters can be separated by a '.' (dot) to allow for retrieval of nested config keys, e.g,\n        'execution.generator.name'\n\n        This function calls an identically named function in a `ConfigManager` instance.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        config_key: str\n            A key of interest present in the config dict\n\n        Returns\n        -------\n        dict\n            a dictionary from the part of the config file corresponding to `model_id` and `config_key`.\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        return self.config_manager.get_config_by_id(\n            model_id=model_id, config_key=config_key\n        )\n\n    def is_model_metadata_valid(\n        self, model_id: str, metadata: dict, is_local_model: bool = True\n    ) -> bool:\n        \"\"\"Checking if a model's corresponding metadata is valid.\n\n        Specific fields in the model's metadata are mandatory. It is asserted if these key value pairs are present.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        metadata: dict\n            The model's corresponding metadata\n        is_local_model: bool\n            flag indicating whether the tested model is a new local user model i.e not yet part of medigan's official models\n\n        Returns\n        -------\n        bool\n            Flag indicating whether the specific model's metadata format and fields are valid\n        \"\"\"\n\n        return self.config_manager.is_model_metadata_valid(\n            model_id=model_id, metadata=metadata, is_local_model=is_local_model\n        )\n\n    def add_model_to_config(\n        self,\n        model_id: str,\n        metadata: dict,\n        is_local_model: bool = None,\n        overwrite_existing_metadata: bool = False,\n        store_new_config: bool = True,\n    ) -> bool:\n        \"\"\"Adding or updating a model entry in the global metadata.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        metadata: dict\n            The model's corresponding metadata\n        is_local_model: bool\n            flag indicating whether the tested model is a new local user model i.e not yet part of medigan's official models\n        overwrite_existing_metadata: bool\n            in case of `is_local_model`, flag indicating whether existing metadata for this model in medigan's `config/global.json` should be overwritten.\n        store_new_config: bool\n            flag indicating whether the current model metadata should be stored on disk i.e. in config/\n\n        Returns\n        -------\n        bool\n            Flag indicating whether model metadata update was successfully concluded\n        \"\"\"\n\n        if is_local_model is None:\n            model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n            # if no model contributor can be found the model is assumed to be not a local model.\n            is_local_model = not is_local_model == self.get_model_contributor_by_id(\n                model_id=model_id\n            )\n\n        return self.config_manager.add_model_to_config(\n            model_id=model_id,\n            metadata=metadata,\n            is_local_model=is_local_model,\n            overwrite_existing_metadata=overwrite_existing_metadata,\n            store_new_config=store_new_config,\n        )\n\n    ############################ MODEL SELECTOR METHODS ############################\n\n    def list_models(self) -> list:\n        \"\"\"Return the list of model_ids as strings based on config.\n\n        Returns\n        -------\n        list\n        \"\"\"\n\n        return [model_id for model_id in self.config_manager.model_ids]\n\n    def get_selection_criteria_by_id(\n        self, model_id: str, is_model_id_removed: bool = True\n    ) -> dict:\n        \"\"\"Get and return the selection config dict for a specific model_id.\n\n        This function calls an identically named function in a `ModelSelector` instance.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        is_model_id_removed: bool\n            flag to to remove the model_ids from first level of dictionary.\n\n        Returns\n        -------\n        dict\n            a dictionary corresponding to the selection config of a model\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        return self.model_selector.get_selection_criteria_by_id(model_id=model_id)\n\n    def get_selection_criteria_by_ids(\n        self, model_ids: list = None, are_model_ids_removed: bool = True\n    ) -> list:\n        \"\"\"Get and return a list of selection config dicts for each of the specified model_ids.\n\n        This function calls an identically named function in a `ModelSelector` instance.\n\n        Parameters\n        ----------\n        model_ids: list\n            A list of generative models' unique ids or ids abbreviated as integers (e.g. 1, 2, .. 21)\n        are_model_ids_removed: bool\n            flag to remove the model_ids from first level of dictionary.\n\n        Returns\n        -------\n        list\n            a list of dictionaries each corresponding to the selection config of a model\n        \"\"\"\n\n        mapped_model_ids = []\n        for model_id in model_ids:\n            mapped_model_ids.append(\n                self.config_manager.match_model_id(provided_model_id=model_id)\n            )\n\n        return self.model_selector.get_selection_criteria_by_ids(\n            model_ids=mapped_model_ids, are_model_ids_removed=are_model_ids_removed\n        )\n\n    def get_selection_values_for_key(self, key: str, model_id: str = None) -> list:\n        \"\"\"Get and return the value of a specified key of the selection dict in the config for a specific model_id.\n\n        The key param can contain '.' (dot) separations to allow for retrieval of nested config keys such as\n        'execution.generator.name'\n\n        This function calls an identically named function in a `ModelSelector` instance.\n\n        Parameters\n        ----------\n        key: str\n            The key in the selection dict\n        model_id: str\n            The generative model's unique id\n\n        Returns\n        -------\n        list\n            a list of the values that correspond to the key in the selection config of the `model_id`.\n        \"\"\"\n\n        return self.model_selector.get_selection_values_for_key(\n            key=key, model_id=model_id\n        )\n\n    def get_selection_keys(self, model_id: str = None) -> list:\n        \"\"\"Get and return all first level keys from the selection config dict for a specific model_id.\n\n        This function calls an identically named function in a `ModelSelector` instance.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n\n        Returns\n        -------\n        list\n            a list containing the keys as strings of the selection config of the `model_id`.\n        \"\"\"\n\n        return self.model_selector.get_selection_keys(model_id=model_id)\n\n    def get_models_by_key_value_pair(\n        self, key1: str, value1: str, is_case_sensitive: bool = False\n    ) -> list:\n        \"\"\"Get and return a list of model_id dicts that contain the specified key value pair in their selection config.\n\n        The key param can contain '.' (dot) separations to allow for retrieval of nested config keys such as\n        'execution.generator.name'\n\n        This function calls an identically named function in a `ModelSelector` instance.\n\n        Parameters\n        ----------\n        key1: str\n            The key in the selection dict\n        value1: str\n            The value in the selection dict that corresponds to key1\n        is_case_sensitive: bool\n            flag to evaluate keys and values with case sensitivity if set to True\n\n        Returns\n        -------\n        list\n            a list of the dictionaries each containing a models id and the found key-value pair in the models config\n        \"\"\"\n\n        return self.model_selector.get_models_by_key_value_pair(\n            key1=key1, value1=value1, is_case_sensitive=is_case_sensitive\n        )\n\n    def rank_models_by_performance(\n        self, model_ids: list = None, metric: str = \"SSIM\", order: str = \"asc\"\n    ) -> list:\n        \"\"\"Rank model based on a provided metric and return sorted list of model dicts.\n\n        The metric param can contain '.' (dot) separations to allow for retrieval of nested metric config keys such as\n        'downstream_task.CLF.accuracy'\n\n        This function calls an identically named function in a `ModelSelector` instance.\n\n        Parameters\n        ----------\n        model_ids: list\n            only evaluate the `model_ids` in this list. If none, evaluate all available `model_ids`\n        metric: str\n            The key in the selection dict that corresponds to the metric of interest\n        order: str\n            the sorting order of the ranked results. Should be either \"asc\" (ascending) or \"desc\" (descending)\n\n        Returns\n        -------\n        list\n            a list of model dictionaries containing metric and `model_id`, sorted by metric.\n        \"\"\"\n\n        return self.model_selector.rank_models_by_performance(\n            model_ids=model_ids, metric=metric, order=order\n        )\n\n    def find_matching_models_by_values(\n        self,\n        values: list,\n        target_values_operator: str = \"AND\",\n        are_keys_also_matched: bool = False,\n        is_case_sensitive: bool = False,\n    ) -> list:\n        \"\"\"Search for values (and keys) in model configs and return a list of each matching `ModelMatchCandidate`.\n\n        This function calls an identically named function in a `ModelSelector` instance.\n\n        Parameters\n        ----------\n        values: list\n            list of values used to search and find models corresponding to these `values`\n        target_values_operator: str\n            the operator indicating the relationship between `values` in the evaluation of model search results.\n            Should be either \"AND\", \"OR\", or \"XOR\".\n        are_keys_also_matched: bool\n            flag indicating whether, apart from values, the keys in the model config should also be searchable\n        is_case_sensitive: bool\n            flag indicating whether the search for values (and) keys in the model config should be case-sensitive.\n\n        Returns\n        -------\n        list\n            a list of `ModelMatchCandidate` class instances each of which was successfully matched against the search\n            values.\n        \"\"\"\n\n        return self.model_selector.find_matching_models_by_values(\n            values=values,\n            target_values_operator=target_values_operator,\n            are_keys_also_matched=are_keys_also_matched,\n            is_case_sensitive=is_case_sensitive,\n        )\n\n    def find_models_and_rank(\n        self,\n        values: list,\n        target_values_operator: str = \"AND\",\n        are_keys_also_matched: bool = False,\n        is_case_sensitive: bool = False,\n        metric: str = \"SSIM\",\n        order: str = \"asc\",\n    ) -> list:\n        \"\"\"Search for values (and keys) in model configs, rank results and return sorted list of model dicts.\n\n        This function calls an identically named function in a `ModelSelector` instance.\n\n        Parameters\n        ----------\n        values: list`\n            list of values used to search and find models corresponding to these `values`\n        target_values_operator: str\n            the operator indicating the relationship between `values` in the evaluation of model search results.\n            Should be either \"AND\", \"OR\", or \"XOR\".\n        are_keys_also_matched: bool\n            flag indicating whether, apart from values, the keys in the model config should also be searchable\n        is_case_sensitive: bool\n            flag indicating whether the search for values (and) keys in the model config should be case-sensitive.\n        metric: str\n            The key in the selection dict that corresponds to the metric of interest\n        order: str\n            the sorting order of the ranked results. Should be either \"asc\" (ascending) or \"desc\" (descending)\n\n        Returns\n        -------\n        list\n            a list of the searched and matched model dictionaries containing metric and model_id, sorted by metric.\n        \"\"\"\n        ranked_models = []\n        matching_models = self.model_selector.find_matching_models_by_values(\n            values=values,\n            target_values_operator=target_values_operator,\n            are_keys_also_matched=are_keys_also_matched,\n            is_case_sensitive=is_case_sensitive,\n        )\n        if len(matching_models) < 1:\n            logging.warning(\n                f\"For your input, there were {len(matching_models)} matching models, while at least 1 is needed. \"\n                f\"Please adjust either your metric your search value inputs {values} to find at least one match.\"\n            )\n        else:\n            matching_model_ids = [model.model_id for model in matching_models]\n            logging.debug(f\"matching_model_ids: {matching_model_ids}\")\n            ranked_models = self.model_selector.rank_models_by_performance(\n                model_ids=matching_model_ids, metric=metric, order=order\n            )\n            if len(ranked_models) < 1:\n                logging.warning(\n                    f\"None ({len(ranked_models)}) of the {len(matching_model_ids)} found matching models, had a valid metric entry for {metric}. \"\n                    f\"Please adjust your metric to enable ranking of the found models.\"\n                )\n        return ranked_models\n\n    def find_models_rank_and_generate(\n        self,\n        values: list,\n        target_values_operator: str = \"AND\",\n        are_keys_also_matched: bool = False,\n        is_case_sensitive: bool = False,\n        metric: str = \"SSIM\",\n        order: str = \"asc\",\n        num_samples: int = 30,\n        output_path: str = None,\n        is_gen_function_returned: bool = False,\n        install_dependencies: bool = False,\n        **kwargs,\n    ):\n        \"\"\"Search for values (and keys) in model configs, rank results to generate samples with highest ranked model.\n\n        Parameters\n        ----------\n        values: list\n            list of values used to search and find models corresponding to these `values`\n        target_values_operator: str\n            the operator indicating the relationship between `values` in the evaluation of model search results.\n            Should be either \"AND\", \"OR\", or \"XOR\".\n        are_keys_also_matched: bool\n            flag indicating whether, apart from values, the keys in the model config should also be searchable\n        is_case_sensitive: bool\n            flag indicating whether the search for values (and) keys in the model config should be case-sensitive.\n        metric: str\n            The key in the selection dict that corresponds to the metric of interest\n        order: str\n            the sorting order of the ranked results. Should be either \"asc\" (ascending) or \"desc\" (descending)\n        num_samples: int\n            the number of samples that will be generated\n        output_path: str\n            the path as str to the output folder where the generated samples will be stored\n        is_gen_function_returned: bool\n            flag indicating whether, instead of generating samples, the sample generation function will be returned\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n        **kwargs\n            arbitrary number of keyword arguments passed to the model's sample generation function\n\n        Returns\n        -------\n        None\n            However, if `is_gen_function_returned` is True, it returns the internal generate function of the model.\n        \"\"\"\n        ranked_models = self.find_models_and_rank(\n            values=values,\n            target_values_operator=target_values_operator,\n            are_keys_also_matched=are_keys_also_matched,\n            is_case_sensitive=is_case_sensitive,\n            metric=metric,\n            order=order,\n        )\n\n        assert ranked_models is not None and len(ranked_models) > 0, (\n            f\"None of the models fulfilled both, the matching (values: {values}) AND \"\n            f\"ranking (metric: {metric}) criteria you provided.\"\n        )\n\n        # Get the ID of the highest ranking model to generate() with that model\n        highest_ranking_model_id = ranked_models[0][MODEL_ID]\n\n        # Let's generate with the best-ranked model\n        logging.info(\n            f\"For your input, there were {len(ranked_models)} models found and ranked. \"\n            f\"The highest ranked model ({highest_ranking_model_id}) will now be used for generation: \"\n            f\"{ranked_models[0]}\"\n        )\n\n        return self.generate(\n            model_id=highest_ranking_model_id,\n            num_samples=num_samples,\n            output_path=output_path,\n            is_gen_function_returned=is_gen_function_returned,\n            install_dependencies=install_dependencies,\n            **kwargs,\n        )\n\n    def find_model_and_generate(\n        self,\n        values: list,\n        target_values_operator: str = \"AND\",\n        are_keys_also_matched: bool = False,\n        is_case_sensitive: bool = False,\n        num_samples: int = 30,\n        output_path: str = None,\n        is_gen_function_returned: bool = False,\n        install_dependencies: bool = False,\n        **kwargs,\n    ):\n        \"\"\"Search for values (and keys) in model configs to generate samples with the found model.\n\n        Note that the number of found models should be ==1. Else no samples will be generated and a error is logged to\n        console.\n\n        Parameters\n        ----------\n        values: list\n            list of values used to search and find models corresponding to these `values`\n        target_values_operator: str\n            the operator indicating the relationship between `values`  in the evaluation of model search results.\n            Should be either \"AND\", \"OR\", or \"XOR\".\n        are_keys_also_matched: bool\n            flag indicating whether, apart from values, the keys in the model config should also be searchable\n        is_case_sensitive: bool\n            flag indicating whether the search for values (and) keys in the model config should be case-sensitive.\n        num_samples: int\n            the number of samples that will be generated\n        output_path: str\n            the path as str to the output folder where the generated samples will be stored\n        is_gen_function_returned: bool\n            flag indicating whether, instead of generating samples, the sample generation function will be returned\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n        **kwargs\n            arbitrary number of keyword arguments passed to the model's sample generation function\n\n        Returns\n        -------\n        None\n            However, if `is_gen_function_returned` is True, it returns the internal generate function of the model.\n        \"\"\"\n\n        matching_models: list = self.model_selector.find_matching_models_by_values(\n            values=values,\n            target_values_operator=target_values_operator,\n            are_keys_also_matched=are_keys_also_matched,\n            is_case_sensitive=is_case_sensitive,\n        )\n        if len(matching_models) > 1:\n            logging.error(\n                f\"For your input, there were more than 1 matching model ({len(matching_models)}). \"\n                f\"Please choose one of the models (see model_ids below) or use find_models_rank_and_generate() instead.\"\n                f\"Alternatively, you may also further specify additional search values apart from the provided ones \"\n                f\"to find exactly one model: {values}. The matching models were the following: \\n {matching_models}\"\n            )\n        elif len(matching_models) < 1:\n            logging.error(\n                f\"For your input, there were {len(matching_models)} matching models, while 1 is needed. \"\n                f\"Please adjust your search value inputs {values} to find at least one match.\"\n            )\n        else:\n            # Exactly one matching model. Let's generate with this model\n            logging.info(\n                f\"For your input, there was {len(matching_models)} model matched. \"\n                f\"This model will now be used for generation: {matching_models}\"\n            )\n            matched_model_id = matching_models[0].model_id\n            return self.generate(\n                model_id=matched_model_id,\n                num_samples=num_samples,\n                output_path=output_path,\n                is_gen_function_returned=is_gen_function_returned,\n                install_dependencies=install_dependencies,\n                **kwargs,\n            )\n\n    ############################ MODEL EXECUTOR METHODS ############################\n\n    def add_all_model_executors(self):\n        \"\"\"Add `ModelExecutor` class instances for all models available in the config.\n\n        Returns\n        -------\n        None\n        \"\"\"\n\n        for model_id in self.config_manager.model_ids:\n            execution_config = self.config_manager.get_config_by_id(\n                model_id=model_id, config_key=CONFIG_FILE_KEY_EXECUTION\n            )\n            self._add_model_executor(\n                model_id=model_id, execution_config=execution_config\n            )\n\n    def add_model_executor(self, model_id: str, install_dependencies: bool = False):\n        \"\"\"Add one `ModelExecutor` class instance corresponding to the specified `model_id`.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n\n\n        Returns\n        -------\n        None\n        \"\"\"\n\n        if not self.is_model_executor_already_added(model_id):\n            execution_config = self.config_manager.get_config_by_id(\n                model_id=model_id, config_key=CONFIG_FILE_KEY_EXECUTION\n            )\n            self._add_model_executor(\n                model_id=model_id,\n                execution_config=execution_config,\n                install_dependencies=install_dependencies,\n            )\n\n    def _add_model_executor(\n        self, model_id: str, execution_config: dict, install_dependencies: bool = False\n    ):\n        \"\"\"Add one `ModelExecutor` class instance corresponding to the specified `model_id` and `execution_config`.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        execution_config: dict\n            The part of the config below the 'execution' key\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n\n        Returns\n        -------\n        None\n        \"\"\"\n\n        if not self.is_model_executor_already_added(model_id):\n            model_executor = ModelExecutor(\n                model_id=model_id,\n                execution_config=execution_config,\n                download_package=True,\n                install_dependencies=install_dependencies,\n            )\n            self.model_executors.append(model_executor)\n\n    def is_model_executor_already_added(self, model_id) -> bool:\n        \"\"\"Check whether the `ModelExecutor` instance of this model_id is already in `self.model_executors` list.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n\n        Returns\n        -------\n        bool\n            indicating whether this `ModelExecutor` had been already previously added to `self.model_executors`\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        if self.find_model_executor_by_id(model_id=model_id) is None:\n            logging.debug(\n                f\"{model_id}: The model has not yet been added to the model_executor list.\"\n            )\n            return False\n        return True\n\n    def find_model_executor_by_id(self, model_id: str) -> ModelExecutor:\n        \"\"\"Find and return the `ModelExecutor` instance of this model_id in the `self.model_executors` list.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n\n        Returns\n        -------\n        ModelExecutor\n            `ModelExecutor` class instance corresponding to the `model_id`\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        for idx, model_executor in enumerate(self.model_executors):\n            if model_executor.model_id == model_id:\n                return model_executor\n        return None\n\n    def get_model_executor(\n        self, model_id: str, install_dependencies: bool = False\n    ) -> ModelExecutor:\n        \"\"\"Add and return the `ModelExecutor` instance of this model_id from the `self.model_executors` list.\n\n        Relies on `self.add_model_executor` and `self.find_model_executor_by_id` functions.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n\n        Returns\n        -------\n        ModelExecutor\n            `ModelExecutor` class instance corresponding to the `model_id`\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        try:\n            self.add_model_executor(\n                model_id=model_id,\n                install_dependencies=install_dependencies,\n            )  # only adds after checking that is not already added\n            return self.find_model_executor_by_id(model_id=model_id)\n        except Exception as e:\n            logging.error(\n                f\"{model_id}: This model could not be added to model_executor list: {e}\"\n            )\n            raise e\n\n    def generate(\n        self,\n        model_id: str,\n        num_samples: int = 30,\n        output_path: str = None,\n        save_images: bool = True,\n        is_gen_function_returned: bool = False,\n        install_dependencies: bool = False,\n        **kwargs,\n    ):\n        \"\"\"Generate samples with the model corresponding to the `model_id` or return the model's generate function.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        num_samples: int\n            the number of samples that will be generated\n        output_path: str\n            the path as str to the output folder where the generated samples will be stored\n        save_images: bool\n            flag indicating whether generated samples are returned (i.e. as list of numpy arrays) or rather stored in file system (i.e in `output_path`)\n        is_gen_function_returned: bool\n            flag indicating whether, instead of generating samples, the sample generation function will be returned\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n        **kwargs\n            arbitrary number of keyword arguments passed to the model's sample generation function\n\n        Returns\n        -------\n        list\n            Returns images as list of numpy arrays if `save_images` is False. However, if `is_gen_function_returned` is True, it returns the internal generate function of the model.\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        model_executor = self.get_model_executor(\n            model_id=model_id, install_dependencies=install_dependencies\n        )\n        return model_executor.generate(\n            num_samples=num_samples,\n            output_path=output_path,\n            save_images=save_images,\n            is_gen_function_returned=is_gen_function_returned,\n            **kwargs,\n        )\n\n    def get_generate_function(\n        self,\n        model_id: str,\n        num_samples: int = 30,\n        output_path: str = None,\n        install_dependencies: bool = False,\n        **kwargs,\n    ):\n        \"\"\"Return the model's generate function.\n\n        Relies on the `self.generate` function.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        num_samples: int\n            the number of samples that will be generated\n        output_path: str\n            the path as str to the output folder where the generated samples will be stored\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n        **kwargs\n            arbitrary number of keyword arguments passed to the model's sample generation function\n\n        Returns\n        -------\n        function\n            The internal reusable generate function of the generative model.\n        \"\"\"\n\n        return self.generate(\n            model_id=model_id,\n            num_samples=num_samples,\n            output_path=output_path,\n            is_gen_function_returned=True,\n            install_dependencies=install_dependencies,\n            **kwargs,\n        )\n\n    ############################ MODEL CONTRIBUTOR METHODS ############################\n\n    def add_model_contributor(\n        self,\n        model_id: str,\n        init_py_path: str = None,\n    ) -> ModelContributor:\n        \"\"\"Add a `ModelContributor` instance of this model_id to the `self.model_contributors` list.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        init_py_path: str\n            The path to the local model's __init__.py file needed for importing and running this model.\n\n        Returns\n        -------\n        ModelContributor\n            `ModelContributor` class instance corresponding to the `model_id`\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        model_contributor = self.get_model_contributor_by_id(model_id=model_id)\n        if model_contributor is not None:\n            logging.warning(\n                f\"{model_id}: For this model_id, there already exists a ModelContributor. None was added. Returning the existing one.\"\n            )\n        else:\n            model_contributor = ModelContributor(\n                model_id=model_id, init_py_path=init_py_path\n            )\n            self.model_contributors.append(model_contributor)\n        return model_contributor\n\n    def get_model_contributor_by_id(self, model_id: str) -> ModelContributor:\n        \"\"\"Find and return the `ModelContributor` instance of this model_id in the `self.model_contributors` list.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n\n        Returns\n        -------\n        ModelContributor\n            `ModelContributor` class instance corresponding to the `model_id`\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        for idx, model_contributor in enumerate(self.model_contributors):\n            if model_contributor.model_id == model_id:\n                return model_contributor\n        return None\n\n    def add_metadata_from_file(self, model_id: str, metadata_file_path: str) -> dict:\n        \"\"\"Read and parse the metadata of a local model, identified by `model_id`, from a metadata file in json format.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        metadata_file_path: str\n            the path pointing to the metadata file\n\n        Returns\n        -------\n        dict\n            Returns a dict containing the contents of parsed metadata json file.\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        model_contributor = self.get_model_contributor_by_id(model_id=model_id)\n        assert (\n            model_contributor is not None\n        ), f\"{model_id}: No model_contributor is initialized for this model_id in Generators. Add a model_contributor first by running 'add_model_contributor()'.\"\n        return model_contributor.add_metadata_from_file(\n            metadata_file_path=metadata_file_path\n        )\n\n    def add_metadata_from_input(\n        self,\n        model_id: str,\n        model_weights_name: str,\n        model_weights_extension: str,\n        generate_method_name: str,\n        dependencies: list,\n        fill_more_fields_interactively: bool = True,\n        output_path: str = \"config\",\n    ) -> dict:\n        \"\"\"Create a metadata dict for a local model, identified by `model_id`, given the necessary minimum metadata contents.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        model_weights_name: str\n            the name of the checkpoint file containing the model's weights\n        model_weights_extension: str\n            the extension (e.g. .pt) of the checkpoint file containing the model's weights\n        generate_method_name: str\n            the name of the sample generation method inside the models __init__.py file\n        dependencies: list\n            the list of dependencies that need to be installed via pip to run the model\n        fill_more_fields_interactively: bool\n            flag indicating whether a user will be interactively asked via command line for further input to fill out missing metadata content\n        output_path: str\n            the path where the created metadata json file will be stored\n\n        Returns\n        -------\n        dict\n            Returns a dict containing the contents of the metadata json file.\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        model_contributor = self.get_model_contributor_by_id(model_id=model_id)\n        assert (\n            model_contributor is not None\n        ), f\"{model_id}: No model_contributor is initialized for this model_id in Generators. Add a model_contributor first by running 'add_model_contributor()'.\"\n        return model_contributor.add_metadata_from_input(\n            model_weights_name=model_weights_name,\n            model_weights_extension=model_weights_extension,\n            generate_method_name=generate_method_name,\n            dependencies=dependencies,\n            fill_more_fields_interactively=fill_more_fields_interactively,\n            output_path=output_path,\n        )\n\n    def push_to_zenodo(\n        self,\n        model_id: str,\n        zenodo_access_token: str,\n        creator_name: str = \"unknown name\",\n        creator_affiliation: str = \"unknown affiliation\",\n        model_description: str = \"\",\n    ) -> str:\n        \"\"\"Upload the model files as zip archive to a public Zenodo repository where the model will be persistently stored.\n\n        Get your Zenodo access token here: https://zenodo.org/account/settings/applications/tokens/new/ (Enable scopes `deposit:actions` and `deposit:write`)\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        zenodo_access_token: str\n            a personal access token in Zenodo linked to a user account for authentication\n        creator_name: str\n            the creator name that will appear on the corresponding Zenodo model upload homepage\n        creator_affiliation: str\n            the creator affiliation that will appear on the corresponding Zenodo model upload homepage\n        model_description: list\n            the model_description that will appear on the corresponding Zenodo model upload homepage\n\n        Returns\n        -------\n        str\n            Returns the url pointing to the corresponding Zenodo model upload homepage\n        \"\"\"\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        model_contributor = self.get_model_contributor_by_id(model_id=model_id)\n        assert (\n            model_contributor is not None\n        ), f\"{model_id}: No model_contributor is initialized for this model_id in Generators. Add a model_contributor first by running 'add_model_contributor()'.\"\n\n        return model_contributor.push_to_zenodo(\n            access_token=zenodo_access_token,\n            creator_name=creator_name,\n            creator_affiliation=creator_affiliation,\n            model_description=model_description,\n        )\n\n    def push_to_github(\n        self,\n        model_id: str,\n        github_access_token: str,\n        package_link: str = None,\n        creator_name: str = \"\",\n        creator_affiliation: str = \"\",\n        model_description: str = \"\",\n    ):\n        \"\"\"Upload the model's metadata inside a github issue to the medigan github repository.\n\n        To add your model to medigan, your metadata will be reviewed on Github and added to medigan's official model metadata\n\n        The medigan repository issues page: https://github.com/RichardObi/medigan/issues\n\n        Get your Github access token here: https://github.com/settings/tokens\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        github_access_token: str\n            a personal access token linked to your github user account, used as means of authentication\n        package_link:\n            a package link\n        creator_name: str\n            the creator name that will appear on the corresponding github issue\n        creator_affiliation: str\n            the creator affiliation that will appear on the corresponding github issue\n        model_description: list\n            the model_description that will appear on the corresponding github issue\n\n        Returns\n        -------\n        str\n            Returns the url pointing to the corresponding issue on github\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        model_contributor = self.get_model_contributor_by_id(model_id=model_id)\n        assert (\n            model_contributor is not None\n        ), f\"{model_id}: No model_contributor is initialized for this model_id in Generators. Add a model_contributor first by running 'add_model_contributor()'.\"\n\n        return model_contributor.push_to_github(\n            access_token=github_access_token,\n            package_link=package_link,\n            creator_name=creator_name,\n            creator_affiliation=creator_affiliation,\n            model_description=model_description,\n        )\n\n    def test_model(\n        self,\n        model_id: str,\n        is_local_model: bool = True,\n        overwrite_existing_metadata: bool = False,\n        store_new_config: bool = True,\n        num_samples: int = 3,\n        install_dependencies: bool = False,\n    ):\n        \"\"\"Test if a model generates and returns a specific number of samples in the correct format\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        is_local_model: bool\n            flag indicating whether the tested model is a new local user model i.e not yet part of medigan's official models\n        overwrite_existing_metadata: bool\n            in case of `is_local_model`, flag indicating whether existing metadata for this model in medigan's `config/global.json` should be overwritten.\n        store_new_config: bool\n            flag indicating whether the current model metadata should be stored on disk i.e. in config/\n        num_samples: int\n            the number of samples that will be generated\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed.\n            Else error is raised if missing dependencies are detected.\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        if is_local_model:\n            model_contributor = self.get_model_contributor_by_id(model_id=model_id)\n\n            assert model_contributor is not None, (\n                f\"{model_id}: No model_contributor is initialized for this model_id. Try to set 'is_local_model=False'\"\n                f\"or add a model_contributor first by running 'add_model_contributor(model_id, init_py_path)' .\"\n            )\n\n            self.add_model_to_config(\n                model_id=model_id,\n                metadata=model_contributor.metadata,\n                is_local_model=is_local_model,\n                overwrite_existing_metadata=overwrite_existing_metadata,\n                store_new_config=store_new_config,\n            )\n        samples = self.generate(\n            model_id=model_id,\n            save_images=False,\n            install_dependencies=install_dependencies,\n            num_samples=num_samples,\n        )\n        assert (\n            samples is not None\n            and isinstance(samples, list)\n            and (\n                (len(samples) == num_samples) or (len(samples) > num_samples)\n            )  # e.g., len(samples) = num_samples + 1, as sample generation can be restricted to be balanced among classes\n        ), (\n            f\"{model_id}: Model test was not successful. The generated samples {'is None, but ' if samples is None else ''}\"\n            f\"should be a list (actual type: {type(samples)}) and of length {num_samples} (actual length: \"\n            f\"{'None' if samples is None else len(samples)}). Check if input params (e.g. input_path) to model are valid. \"\n        )  # {f'Generated samples: {samples}' if samples is not None else ''}\"\n\n        logging.info(\n            f\"{model_id}: The test of \"\n            f\"{'this new local user model' if is_local_model else 'this existing medigan model'} \"\n            f\"was successful, as model created the expected number ({num_samples}) of synthetic \"\n            f\"samples.\"\n        )\n\n    def contribute(\n        self,\n        model_id: str,\n        init_py_path: str,\n        github_access_token: str,\n        zenodo_access_token: str,\n        metadata_file_path: str = None,\n        model_weights_name: str = None,\n        model_weights_extension: str = None,\n        generate_method_name: str = None,\n        dependencies: list = None,\n        fill_more_fields_interactively: bool = True,\n        overwrite_existing_metadata: bool = False,\n        output_path: str = \"config\",\n        creator_name: str = \"unknown name\",\n        creator_affiliation: str = \"unknown affiliation\",\n        model_description: str = \"\",\n        install_dependencies: bool = False,\n    ):\n        \"\"\"Implements the full model contribution workflow including model metadata generation, model test, model Zenodo upload, and medigan github issue creation.\n\n        Parameters\n        ----------\n        model_id: str\n             The generative model's unique id\n        init_py_path: str\n            The path to the local model's __init__.py file needed for importing and running this model.\n        github_access_token: str\n            a personal access token linked to your github user account, used as means of authentication\n        zenodo_access_token: str\n            a personal access token in Zenodo linked to a user account for authentication\n        metadata_file_path: str\n            the path pointing to the metadata file\n        model_weights_name: str\n            the name of the checkpoint file containing the model's weights\n        model_weights_extension: str\n            the extension (e.g. .pt) of the checkpoint file containing the model's weights\n        generate_method_name: str\n            the name of the sample generation method inside the models __init__.py file\n        dependencies: list\n            the list of dependencies that need to be installed via pip to run the model\n        fill_more_fields_interactively: bool\n            flag indicating whether a user will be interactively asked via command line for further input to fill out missing metadata content\n        overwrite_existing_metadata: bool\n            flag indicating whether existing metadata for this model in medigan's `config/global.json` should be overwritten.\n        output_path: str\n            the path where the created metadata json file will be stored\n        creator_name: str\n            the creator name that will appear on the corresponding github issue\n        creator_affiliation: str\n            the creator affiliation that will appear on the corresponding github issue\n        model_description: list\n            the model_description that will appear on the corresponding github issue\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed.\n            Else error is raised if missing dependencies are detected.\n\n        Returns\n        -------\n        str\n            Returns the url pointing to the corresponding issue on github\n        \"\"\"\n\n        # Create model contributor\n        self.add_model_contributor(model_id=model_id, init_py_path=init_py_path)\n\n        # Adding the metadata of the model from input\n        if metadata_file_path is not None:\n            # Using an existing metadata json\n            metadata = self.add_metadata_from_file(\n                model_id=model_id, metadata_file_path=metadata_file_path\n            )\n        else:\n            # Creating the metadata json\n            metadata = self.add_metadata_from_input(\n                model_id=model_id,\n                model_weights_name=model_weights_name,\n                model_weights_extension=model_weights_extension,\n                generate_method_name=generate_method_name,\n                dependencies=dependencies,\n                fill_more_fields_interactively=fill_more_fields_interactively,\n                output_path=output_path,\n            )\n        logging.debug(\n            f\"{model_id}: The following model metadata was created: {metadata}\"\n        )\n\n        try:\n            self.test_model(\n                model_id=model_id,\n                is_local_model=True,\n                overwrite_existing_metadata=overwrite_existing_metadata,\n                install_dependencies=install_dependencies,\n            )\n        except Exception as e:\n            logging.error(\n                f\"{model_id}: Error while testing this local model. \"\n                f\"Please revise and run model contribute() again. {e}\"\n            )\n            raise e\n\n        # Model Upload to Zenodo\n        zenodo_record_url = self.push_to_zenodo(\n            model_id=model_id,\n            zenodo_access_token=zenodo_access_token,\n            creator_name=creator_name,\n            creator_affiliation=creator_affiliation,\n            model_description=model_description,\n        )\n\n        # Creating and returning an issue with model metadata in medigan's Github\n        return self.push_to_github(\n            model_id=model_id,\n            package_link=zenodo_record_url,\n            github_access_token=github_access_token,\n            creator_name=creator_name,\n            creator_affiliation=creator_affiliation,\n            model_description=model_description,\n        )\n\n    ############################ OTHER METHODS ############################\n\n    def get_as_torch_dataloader(\n        self,\n        dataset=None,\n        model_id: str = None,\n        num_samples: int = 1000,\n        install_dependencies: bool = False,\n        transform=None,\n        batch_size=None,\n        shuffle=None,\n        sampler=None,\n        batch_sampler=None,\n        num_workers=None,\n        collate_fn=None,\n        pin_memory=None,\n        drop_last=None,\n        timeout=None,\n        worker_init_fn=None,\n        prefetch_factor: int = None,\n        persistent_workers: bool = None,\n        pin_memory_device: str = None,\n        **kwargs,\n    ) -> DataLoader:\n        \"\"\"Get torch Dataloader sampling synthetic data from medigan model.\n\n        Dataloader combines a dataset and a sampler, and provides an iterable over\n        the given torch dataset. Dataloader is created for synthetic data for the specified medigan model.\n        Pytorch native parameters are set to ``None`` per default. Only those params are are passed to the Dataloader()\n        initialization function that are not ``None``.\n\n        Args:\n            dataset (Dataset): dataset from which to load the data.\n            model_id: str\n                The generative model's unique id\n            num_samples: int\n                the number of samples that will be generated\n            install_dependencies: bool\n                flag indicating whether a generative model's dependencies are automatically installed.\n                Else error is raised if missing dependencies are detected.\n            **kwargs\n                arbitrary number of keyword arguments passed to the model's sample generation function\n                (e.g. the input path for image-to-image translation models in medigan).\n            transform\n                the torch data transformation functions to be applied to the data in the dataset.\n            batch_size (int, optional): how many samples per batch to load\n                (default: ``None``).\n            shuffle (bool, optional): set to ``True`` to have the data reshuffled\n                at every epoch (default: ``None``).\n            sampler (Sampler or Iterable, optional): defines the strategy to draw\n                samples from the dataset. Can be any ``Iterable`` with ``__len__``\n                implemented. If specified, :attr:`shuffle` must not be specified. (default: ``None``)\n            batch_sampler (Sampler or Iterable, optional): like :attr:`sampler`, but\n                returns a batch of indices at a time. Mutually exclusive with\n                :attr:`batch_size`, :attr:`shuffle`, :attr:`sampler`,\n                and :attr:`drop_last`. (default: ``None``)\n            num_workers (int, optional): how many subprocesses to use for data\n                loading. ``0`` means that the data will be loaded in the main process.\n                (default: ``None``)\n            collate_fn (callable, optional): merges a list of samples to form a\n                mini-batch of Tensor(s).  Used when using batched loading from a\n                map-style dataset. (default: ``None``)\n            pin_memory (bool, optional): If ``True``, the data loader will copy Tensors\n                into CUDA pinned memory before returning them.  If your data elements\n                are a custom type, or your :attr:`collate_fn` returns a batch that is a custom type,\n                see the example below. (default: ``None``)\n            drop_last (bool, optional): set to ``True`` to drop the last incomplete batch,\n                if the dataset size is not divisible by the batch size. If ``False`` and\n                the size of dataset is not divisible by the batch size, then the last batch\n                will be smaller. (default: ``None``)\n            timeout (numeric, optional): if positive, the timeout value for collecting a batch\n                from workers. Should always be non-negative. (default: ``None``)\n            worker_init_fn (callable, optional): If not ``None``, this will be called on each\n                worker subprocess with the worker id (an int in ``[0, num_workers - 1]``) as\n                input, after seeding and before data loading. (default: ``None``)\n            prefetch_factor (int, optional, keyword-only arg): Number of batches loaded\n                in advance by each worker. ``2`` means there will be a total of\n                2 * num_workers batches prefetched across all workers. (default: ``None``).\n            persistent_workers (bool, optional): If ``True``, the data loader will not shutdown\n                the worker processes after a dataset has been consumed once. This allows to\n                maintain the workers `Dataset` instances alive. (default: ``None``)\n            pin_memory_device (str, optional): the device to pin memory to if ``pin_memory`` is ``True`` (default: ``None``).\n\n        Returns\n        -------\n        DataLoader\n            a torch.utils.data.DataLoader object with data generated by model corresponding to inputted `Dataset` or `model_id`.\n        \"\"\"\n\n        dataset = (\n            self.get_as_torch_dataset(\n                model_id=model_id,\n                num_samples=num_samples,\n                install_dependencies=install_dependencies,\n                transform=transform,\n                **kwargs,\n            )\n            if dataset is None\n            else dataset\n        )\n\n        # Reducing dependency on torch.util.data.DataLoader param default values by passing\n        # only the ones specified by the user.\n        dataloader = Utils.call_without_removable_params(\n            my_callable=DataLoader,\n            removable_param_values=[None],\n            dataset=dataset,\n            batch_size=batch_size,\n            shuffle=shuffle,\n            sampler=sampler,\n            batch_sampler=batch_sampler,\n            num_workers=num_workers,\n            collate_fn=collate_fn,\n            pin_memory=pin_memory,\n            drop_last=drop_last,\n            timeout=timeout,\n            worker_init_fn=worker_init_fn,\n            prefetch_factor=prefetch_factor,\n            persistent_workers=persistent_workers,\n            pin_memory_device=pin_memory_device,\n        )\n\n        return dataloader\n\n    def get_as_torch_dataset(\n        self,\n        model_id: str,\n        num_samples: int = 100,\n        install_dependencies: bool = False,\n        transform=None,\n        **kwargs,\n    ) -> Dataset:\n        \"\"\"Get synthetic data in a torch Dataset for specified medigan model.\n\n        The dataset returns a dict with keys sample (== image), labels (== condition), and mask (== segmentation mask).\n        While key 'sample' is mandatory, the other key value pairs are only returned if applicable to generative model.\n\n        Args:\n           model_id: str\n               The generative model's unique id\n           num_samples: int\n               the number of samples that will be generated\n           install_dependencies: bool\n               flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n            transform\n                the torch data transformation functions to be applied to the data in the dataset.\n           **kwargs\n               arbitrary number of keyword arguments passed to the model's sample generation function (e.g. the input path for image-to-image translation models in medigan).\n\n        Returns\n        -------\n        Dataset\n            a torch.utils.data.Dataset object with data generated by model corresponding to `model_id`.\n        \"\"\"\n\n        data = self.generate(\n            model_id=model_id,\n            num_samples=num_samples,\n            is_gen_function_returned=False,\n            install_dependencies=install_dependencies,\n            save_images=False,  # design decision: temporary storage in memory instead of I/O from disk\n            **kwargs,\n        )\n\n        logging.debug(f\"data: {data}\")\n\n        (\n            samples,\n            masks,\n            other_imaging_output,\n            labels,\n        ) = Utils.split_images_masks_and_labels(data=data, num_samples=num_samples)\n        logging.debug(\n            f\"samples: {samples} \\n masks: {masks} \\n other_imaging_output: {other_imaging_output} \\n labels: {labels}\"\n        )\n\n        return SyntheticDataset(\n            samples=samples,\n            masks=masks,\n            other_imaging_output=other_imaging_output,\n            labels=labels,\n            transform=transform,\n        )\n\n    def visualize(\n        self,\n        model_id: str,\n        slider_grouper: int = 10,\n        auto_close: bool = False,\n        install_dependencies: bool = False,\n    ) -> None:\n        \"\"\"Initialize and run `ModelVisualizer` of this model_id if it is available.\n        It allows to visualize a sample from the model's output.\n        UI window will pop up allowing the user to control the generation parameters (conditional and unconditional ones).\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id to visualize.\n        slider_grouper: int\n            Number of input parameters to group together within one slider.\n        auto_close: bool\n            Flag for closing the user interface automatically after time. Used while testing.\n        install_dependencies: bool\n            flag indicating whether a generative model's dependencies are automatically installed. Else error is raised if missing dependencies are detected.\n\n        \"\"\"\n\n        model_id = self.config_manager.match_model_id(provided_model_id=model_id)\n\n        config = self.get_config_by_id(model_id=model_id)\n        model_executor = self.get_model_executor(\n            model_id=model_id, install_dependencies=install_dependencies\n        )\n\n        ModelVisualizer(model_executor=model_executor, config=config).visualize(\n            slider_grouper=slider_grouper, auto_close=auto_close\n        )\n\n    def __repr__(self):\n        return (\n            f\"Generators(model_ids={self.config_manager.model_ids}, model_executors={self.model_executors}, \"\n            f\"model_selector: {self.model_selector})\"\n        )\n\n    def __len__(self):\n        return len(self.model_executors)\n\n    def __getitem__(self, idx: int):\n        return self.model_executors[idx]\n"
  },
  {
    "path": "src/medigan/model_visualizer.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" `ModelVisualizer` class providing visualizing corresponding model input and model output changes. \"\"\"\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.widgets import Button, Slider\n\n\nclass ModelVisualizer:\n    \"\"\"`ModelVisualizer` class: Visualises synthetic data through a user interface. Depending on a model,\n    it is possible to control the input latent vector values and conditional input.\n\n    Parameters\n    ----------\n    model_executor: ModelExecutor\n        The generative model's executor object\n    config: dict\n        The config dict containing the model metadata\n\n\n    Attributes\n    ----------\n    model_executor: ModelExecutor\n        The generative model's executor object\n    input_latent_vector_size: int\n        Size of the latent vector used as an input for generation\n    conditional: bool\n        Flag for models with conditional input\n    condition: Union[int, float]\n        Value of the conditinal input to the model\n    max_input_value: float\n        Absolute value used for setting latent values input range\n    \"\"\"\n\n    def __init__(self, model_executor, config: None):\n        self.model_executor = model_executor\n        self.model_id = self.model_executor.model_id\n        self.config = config\n        self.num_samples = 1\n        self.max_input_value = 3\n        self.conditional = False\n        self.condition = None\n\n        self.input_latent_vector_size = (\n            self.model_executor.generate_method_input_latent_vector_size\n        )\n        if not self.input_latent_vector_size:\n            raise ValueError(\n                f\"{self.model_id}: Visualization of this model is not supported. Reason: This model does not use a random vector 'z' as input, which is needed for visualization. This is determined via the absence of the 'input_latent_vector_size' variable in this model's metadata in config/global.json.\"\n            )\n\n        self.gen_function = self.model_executor.generate(\n            num_samples=1,\n            save_images=False,\n            is_gen_function_returned=True,\n        )\n        if \"condition\" in self.model_executor.generate_method_args[\"custom\"]:\n            self.conditional = True\n            self.condition = self.model_executor.generate_method_args[\"custom\"][\n                \"condition\"\n            ]\n\n    def visualize(self, slider_grouper: int = 10, auto_close=False):\n        \"\"\"\n        Visualize the model's output. This method is called by the user.\n        It opens up a user interface with available controls.\n\n        Parameters\n        ----------\n        slider_grouper: int\n            Number of input parameters to group together within one slider.\n        auto_close: bool\n            Flag for closing the user interface automatically after time. Used while testing.\n\n        Returns\n        -------\n        None\n        \"\"\"\n        z = np.random.randn(\n            self.num_samples, self.input_latent_vector_size, 1, 1\n        ).astype(np.float32)\n\n        mask = None\n\n        if self.conditional:\n            output = self.gen_function(condition=self.condition, input_latent_vector=z)\n        else:\n            output = self.gen_function(input_latent_vector=z)\n\n        image, mask = self._unpack_output(output)\n\n        images_to_show = 1\n        if mask is not None:\n            images_to_show += 1\n\n        fig, ax = plt.subplots(ncols=images_to_show)\n\n        if images_to_show == 1:\n            ax.axis(\"off\")\n            ax.set_title(\"Generated image\")\n            display = ax.imshow(image, cmap=\"gray\", vmin=0, vmax=255)\n        if images_to_show == 2:\n            ax[0].axis(\"off\")\n            ax[0].set_title(\"Generated image\")\n            display = ax[0].imshow(image, cmap=\"gray\", vmin=0, vmax=255)\n            ax[1].axis(\"off\")\n            ax[1].set_title(\"Generated mask\")\n            display_mask = ax[1].imshow(mask, cmap=\"gray\", vmin=0, vmax=255)\n\n        fig.suptitle(\n            \"Model \" + self.model_id,\n            fontsize=15,\n            # fontweight=\"bold\",\n        )\n        if self.config:\n            plt.text(\n                x=0.5,\n                y=0.88,\n                s=self.config[\"description\"][\"title\"],\n                fontsize=8,\n                ha=\"center\",\n                transform=fig.transFigure,\n                wrap=True,\n            )\n        # adjust the main plot to make room for the sliders\n        plt.subplots_adjust(left=0.45, bottom=0.3, top=0.8)\n\n        padding = 0.03\n        sliders_x = 0.1\n        sliders_y = 0.75\n        sliders_width = 0.25\n        sliders_height = 0.02\n        sliders = []\n        row_index = 0\n\n        if self.conditional:\n            condition_ax = plt.axes(\n                (sliders_x, sliders_y, sliders_width, sliders_height)\n            )\n            condition_slider = Slider(\n                condition_ax,\n                None,\n                0,\n                1,\n                valinit=0.0,\n                valstep=1,\n                initcolor=\"none\",\n                # valfmt=\"%.2f\",\n            )\n            condition_ax.set_title(\"Input condition: \" + output[0][1])\n            row_index += 5\n\n        offset_ax = plt.axes(\n            (sliders_x, sliders_y - row_index * padding, sliders_width, sliders_height)\n        )\n        offset_ax.set_title(\"Input latent vector\")\n        offset_slider = Slider(\n            offset_ax,\n            \"offset\",\n            -self.max_input_value * 2,\n            self.max_input_value * 2,\n            valinit=0.0,\n            initcolor=\"none\",\n            valfmt=\"%.2f\",\n        )\n        row_index += 2\n        # for i in range(int(self.input_latent_vector_size)):\n        for i in range(int(self.input_latent_vector_size / slider_grouper)):\n            axfreq = plt.axes(\n                (\n                    sliders_x,\n                    sliders_y - (i + row_index) * padding,\n                    sliders_width,\n                    sliders_height,\n                )\n            )\n            slider = Slider(\n                axfreq,\n                \"z{}\".format(i + 1),\n                -self.max_input_value,\n                self.max_input_value,\n                valinit=float(z[0][i]),\n                initcolor=\"none\",\n                valfmt=\"%.2f\",\n            )\n            sliders.append(slider)\n\n        text = \"Offset: Add constant value to each latent variable \\\n             \\nInput vector: Modify latent values used to generate image \\\n              \\nSeed: Initialize new random seed for latent vector \\\n              \\nReset: Revert user changes to initial seed values\"\n\n        ax_legend = plt.axes(\n            (\n                0.45,\n                0.19,\n                0.5,\n                0.5,\n            )\n        )\n        ax_legend.axis(\"off\")\n\n        ax_legend.text(0.0, 0.0, text, fontsize=8, va=\"top\", linespacing=2)\n\n        # The function to be called anytime a slider's value changes\n        def update(val):\n            for i, slider in enumerate(sliders):\n                for j in range(10):\n                    z[0][i + j] = slider.val\n\n            if self.conditional:\n                self.condition = condition_slider.val\n                output = self.gen_function(\n                    condition=self.condition, input_latent_vector=z\n                )\n                condition_ax.set_title(\"Input condition: \" + output[0][1])\n            else:\n                output = self.gen_function(input_latent_vector=z)\n\n            image, mask = self._unpack_output(output)\n            if mask is not None:\n                display_mask.set_data(mask)\n            display.set_data(image)\n            fig.canvas.draw_idle()\n\n        # register the update function with each slider\n        for slider in sliders:\n            slider.on_changed(update)\n        if self.conditional:\n            condition_slider.on_changed(update)\n\n        self.offset_old = 0\n\n        def update_offset(val):\n            diff = offset_slider.val - self.offset_old\n            self.offset_old = offset_slider.val\n\n            for i, slider in enumerate(sliders):\n                if slider.val + diff > self.max_input_value:\n                    slider.set_val(self.max_input_value)\n                elif slider.val + diff < -self.max_input_value:\n                    slider.set_val(-self.max_input_value)\n                else:\n                    slider.set_val(slider.val + diff)\n\n                for j in range(10):\n                    z[0][i + j] = slider.val\n\n        offset_slider.on_changed(update_offset)\n\n        # Create a `matplotlib.widgets.Button` to reset the sliders to initial values.\n        resetax = plt.axes([0.77, 0.220, 0.1, 0.04])\n        reset_button = Button(resetax, \"Reset\", hovercolor=\"0.975\")\n        seedax = plt.axes([0.62, 0.220, 0.1, 0.04])\n        seed_button = Button(seedax, \"Seed\", hovercolor=\"0.975\")\n\n        def reset(event):\n            offset_slider.reset()\n            for slider in sliders:\n                slider.reset()\n\n        def new_seed(event):\n            z = np.random.randn(\n                self.num_samples, self.input_latent_vector_size, 1, 1\n            ).astype(np.float32)\n            for slider in sliders:\n                slider.valinit = z[0][sliders.index(slider)]\n            reset(event)\n\n        reset_button.on_clicked(reset)\n        seed_button.on_clicked(new_seed)\n        update(0)\n        if auto_close:\n            plt.show(block=False)\n            plt.pause(1)\n            plt.close()\n        else:\n            plt.show()\n\n    def _unpack_output(self, output) -> tuple:\n        \"\"\"\n        Unpack the output of the generator function\n\n        Parameters\n        ----------\n        output: Union[tuple, np.ndarray]\n            Output of the generator function to unpack into an image and optional mask\n\n        Returns\n        ----------\n        tuple[image, mask]\n            Tuple of the image and mask. Mask is None if no mask was available\n        \"\"\"\n        mask = None\n        if type(output[0]) is tuple:\n            image = output[0][0].squeeze()\n            if type(output[0][1]) is not str:\n                mask = output[0][1].squeeze()\n        else:\n            image = output[0].squeeze()\n\n        return image, mask\n"
  },
  {
    "path": "src/medigan/select_model/__init__.py",
    "content": ""
  },
  {
    "path": "src/medigan/select_model/matched_entry.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\"MatchedEntry class that represents one match of a key value pair of a model's config against a search query. \"\"\"\n\n# Import python native libs\nfrom __future__ import absolute_import\n\nimport json\n\n\nclass MatchedEntry:\n    \"\"\"`MatchedEntry` class: One target key-value pair that matches with a model's selection config.\n\n    Parameters\n    ----------\n    key: str\n        string that represents the matched key in model selection dict\n    value\n        represents the key's matched value in the model selection dict\n    matching_element: str\n        string that was used to match the search value\n\n    Attributes\n    ----------\n    key: str\n        string that represents the matched key in model selection dict\n    value\n        represents the key's matched value in the model selection dict\n    matching_element: str\n        string that was used to match the search value\n    \"\"\"\n\n    def __init__(\n        self,\n        key: str,\n        value,\n        matching_element: str = None,\n    ):\n        self.key = key\n        self.value = value\n        if matching_element is None:\n            self.matching_element = str(value)\n        else:\n            self.matching_element = matching_element\n\n    def __str__(self):\n        return json.dumps(\n            {\n                \"key\": self.key,\n                \"value\": self.value,\n                \"matching_element\": self.matching_element,\n            }\n        )\n\n    def __repr__(self):\n        return f\"MatchedEntry(key={self.key}, value={self.value}, matching_element={self.matching_element})\"\n\n    def __len__(self):\n        raise NotImplementedError\n\n    def __getitem__(self, idx: int):\n        raise NotImplementedError\n"
  },
  {
    "path": "src/medigan/select_model/model_match_candidate.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\"ModelMatchCandidate class that holds data to evaluate if a generative model matches against a search query. \"\"\"\n\n# Import python native libs\nfrom __future__ import absolute_import\n\nimport json\nimport logging\n\n# Import library internal modules\nfrom .matched_entry import MatchedEntry\n\n\nclass ModelMatchCandidate:\n    \"\"\"`ModelMatchCandidate` class: A prospectively matching model given the target values as model search params.\n\n    Parameters\n    ----------\n    model_id: str\n        The generative model's unique id\n    target_values: list\n        list of target values used to evaluate if a `ModelMatchCandidate` instance is a match\n    target_values_operator: str\n        the operator indicating the relationship between `values` in the evaluation of `ModelMatchCandidate` instances.\n        Should be either \"AND\", \"OR\", or \"XOR\".\n    is_case_sensitive: bool\n        flag indicating whether the matching of `values` (and) keys should be case-sensitive\n    are_keys_also_matched: bool\n        flag indicating whether, apart from `values`, keys should also be matched\n    is_match: bool\n        flag indicating whether the `ModelMatchCandidate` instance is a match\n\n    Attributes\n    ----------\n    model_id: str\n        The generative model's unique id\n    target_values: list\n        list of target values used to evaluate if a `ModelMatchCandidate` instance is a match\n    target_values_operator: str\n        the operator indicating the relationship between `values` in the evaluation of `ModelMatchCandidate` instances.\n        Should be either \"AND\", \"OR\", or \"XOR\".\n    is_case_sensitive: bool\n        flag indicating whether the matching of `values` (and) keys should be case-sensitive\n    are_keys_also_matched: bool\n        flag indicating whether, apart from values, keys should also be matched\n    matched_entries: list\n        contains iteratively added `MatchedEntry` class instances. Each of the `MatchedEntry` instances indicates a match\n        between one of the user specified values in `self.target_values` and the selection config keys or `values` of the\n        model of this `ModelMatchCandidate`.\n    is_match: bool\n        flag indicating whether the `ModelMatchCandidate` instance is a match\n    \"\"\"\n\n    def __init__(\n        self,\n        model_id: str,\n        target_values: list,\n        target_values_operator: str = \"AND\",\n        is_case_sensitive: bool = False,\n        are_keys_also_matched: bool = False,\n        is_match: bool = False,\n    ):\n        # Descriptive variables\n        self.model_id = model_id\n        self.target_values = target_values\n        self.target_values_operator = target_values_operator\n        self.is_case_sensitive = is_case_sensitive\n        self.are_keys_also_matched = are_keys_also_matched\n\n        # Dynamically filled/changed variables\n        self.matched_entries = []\n        self.is_match = is_match\n\n    def add_matched_entry(self, matched_entry: MatchedEntry) -> None:\n        \"\"\"Add a `MatchedEntry` instance to the `matched_entries` list.\"\"\"\n        self.matched_entries.append(matched_entry)\n\n    def get_all_matching_elements(self) -> list:\n        \"\"\"Get the matching element from each of the `MatchedEntry` instances in the `matched_entries` list.\n\n        Returns\n        -------\n        list\n            list of all matching elements (i.e. string that matched a search value) from each `MatchedEntry` in\n            `matched_entries`\n        \"\"\"\n\n        matching_elements = []\n        for matched_entry in self.matched_entries:\n            matching_elements.append(matched_entry.matching_element)\n        return matching_elements\n\n    def check_if_is_match(self) -> bool:\n        \"\"\"Evaluates whether the model represented by this instance is a match given search values and operator.\n\n        The matching element from each `MatchEntry` of this instance ('self.matched_entries') are retrieved. To be a\n        match, this instance ('self') needs to fulfill the requirement of the operator, which can be of value 'AND',\n        or 'OR', or 'XOR'. For example, the default 'AND' requires that each search value ('self.target_values') has at\n        least one corresponding `MatchEntry`, while in 'OR' only one of the search values needs to have been matched by a\n        corresponding `MatchedEntry`.\n\n        Returns\n        -------\n        bool\n            flag that, only if True, indicates that this instance is a match given the search values and operator.\n        \"\"\"\n\n        if self is not None and len(self) > 0:\n            if self.target_values_operator == \"OR\":\n                self.is_match = True\n            elif self.target_values_operator == \"AND\":\n                # removing duplicates via set conversion\n                found_target_values = set(self.get_all_matching_elements())\n                if all(elem in found_target_values for elem in self.target_values):\n                    logging.debug(\n                        f\"values: {self.target_values} AND found_target_values_list: {found_target_values}\"\n                    )\n                    self.is_match = True\n            elif self.target_values_operator == \"XOR\":\n                # removing duplicates via set conversion\n                if (\n                    len(\n                        list(\n                            set(self.get_all_matching_elements()).intersection(\n                                self.target_values\n                            )\n                        )\n                    )\n                    == 1\n                ):\n                    self.is_match = True\n        logging.debug(f\"This ModelMatchCandidate was found to be a match: ({self}).\")\n        return self.is_match\n\n    def __str__(self):\n        matched_entry_dicts = {\n            f\"{idx}\": json.loads(str(match))\n            for idx, match in enumerate(self.matched_entries)\n        }\n        return json.dumps(\n            {\n                \"model_id\": self.model_id,\n                \"is_match\": self.is_match,\n                \"target_values\": self.target_values,\n                \"operator\": self.target_values_operator,\n                \"are_keys_also_matched\": self.are_keys_also_matched,\n                \"is_case_sensitive\": self.is_case_sensitive,\n                \"matched_entries\": matched_entry_dicts,\n            }\n        )\n\n    def __repr__(self):\n        return f\"ModelMatchCandidate(model_id={self.model_id}, is_match={self.is_match}, operator: {self.target_values_operator}, target_values={self.target_values})\"\n\n    def __len__(self):\n        return len(self.matched_entries)\n\n    def __getitem__(self, idx: int):\n        return self.matched_entries[idx]\n"
  },
  {
    "path": "src/medigan/select_model/model_selector.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" Model selection class that describes, finds, compares, and ranks generative models. \"\"\"\n\n# Import python native libs\nfrom __future__ import absolute_import\n\nimport logging\n\n# Import library internal modules\nfrom ..config_manager import ConfigManager\nfrom ..constants import CONFIG_FILE_KEY_PERFORMANCE, CONFIG_FILE_KEY_SELECTION, MODEL_ID\nfrom ..utils import Utils\nfrom .matched_entry import MatchedEntry\nfrom .model_match_candidate import ModelMatchCandidate\n\n\nclass ModelSelector:\n    \"\"\"`ModelSelector` class: Given a config dict, gets, searches, and ranks matching models.\n\n    Parameters\n    ----------\n    config_manager: ConfigManager\n        Provides the config dictionary, based on which models are selected and compared.\n\n    Attributes\n    ----------\n    config_manager: ConfigManager\n        Provides the config dictionary, based on which models are selected and compared.\n    model_selection_dicts: list\n        Contains a dictionary for each model id that consists of the `model_id` and the selection config of that model\n    \"\"\"\n\n    def __init__(\n        self,\n        config_manager: ConfigManager = None,\n    ):\n        if config_manager is None:\n            self.config_manager = ConfigManager()\n            logging.debug(f\"Initialized ConfigManager instance: {self.config_manager}\")\n        else:\n            self.config_manager = config_manager\n        self.model_selection_dicts = []\n        self._init_model_selector_data()\n\n    def _init_model_selector_data(self):\n        \"\"\"Initialize class data structure: List of dicts containing two keys each: `model_id` and `selection`.\"\"\"\n        for model_id in self.config_manager.model_ids:\n            selection_config = self.config_manager.get_config_by_id(\n                model_id=model_id, config_key=CONFIG_FILE_KEY_SELECTION\n            )\n            model_selector_dict = {\n                MODEL_ID: model_id,\n                CONFIG_FILE_KEY_SELECTION: selection_config,\n            }\n            self.model_selection_dicts.append(model_selector_dict)\n        logging.debug(\n            f\"These were the available model selection dicts that were added to the ModelSelector: \"\n            f\"{self.model_selection_dicts}.\"\n        )\n\n    def get_selection_criteria_by_id(\n        self, model_id: str, is_model_id_removed: bool = True\n    ) -> dict:\n        \"\"\"Get and return the selection config dict for a specific `model_id`.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n        is_model_id_removed: bool\n            flag to to remove the `model_id` from first level of each dictionary.\n\n        Returns\n        -------\n        dict\n            a dictionary corresponding to the selection config of a model\n        \"\"\"\n        for idx, selection_dict in enumerate(self.model_selection_dicts):\n            if selection_dict[MODEL_ID] == model_id:\n                if is_model_id_removed:\n                    logging.debug(\n                        f\"For model {model_id}, the following selection dicts was found:\"\n                        f\" {selection_dict[CONFIG_FILE_KEY_SELECTION]}.\"\n                    )\n                    return selection_dict[CONFIG_FILE_KEY_SELECTION]\n                else:\n                    logging.debug(\n                        f\"For model {model_id}, the following selection dicts was found:\"\n                        f\" {selection_dict}.\"\n                    )\n                    return selection_dict\n        return None\n\n    def get_selection_criteria_by_ids(\n        self, model_ids: list = None, are_model_ids_removed: bool = True\n    ) -> list:\n        \"\"\"Get and return a list of selection config dicts for each of the specified `model_ids`.\n\n        Parameters\n        ----------\n        model_ids: list\n            A list of generative models' unique ids\n        are_model_ids_removed: bool\n            flag to remove the `model_ids` from first level of dictionary.\n\n        Returns\n        -------\n        list\n            a list of dictionaries each corresponding to the selection config of a model\n        \"\"\"\n        # Create list of models that contain a value for the metric of interest\n        selection_dict_list = []\n        for idx, selection_dict in enumerate(self.model_selection_dicts):\n            if model_ids is None or selection_dict[MODEL_ID] in model_ids:\n                # if model_ids is None, we consider all models\n                if are_model_ids_removed:\n                    selection_dict_list.append(\n                        selection_dict[CONFIG_FILE_KEY_SELECTION]\n                    )\n                else:\n                    selection_dict_list.append(selection_dict)\n        logging.debug(\n            f\"The following selection dicts were found: {selection_dict_list}.\"\n        )\n        return selection_dict_list\n\n    def get_selection_keys(self, model_id: str = None) -> list:\n        \"\"\"Get and return all first level keys from the selection config dict for a specific `model_id`.\n\n        Parameters\n        ----------\n        model_id: str\n            The generative model's unique id\n\n        Returns\n        -------\n        list\n            a list containing the keys as strings of the selection config of the `model_id`.\n        \"\"\"\n\n        key_list = []\n        if model_id is not None:\n            selection_config = self.get_selection_criteria_by_id(model_id)\n            for key in selection_config:\n                key_list.append(key)\n        else:\n            for selection_dict in self.model_selection_dicts:\n                selection_config = selection_dict[CONFIG_FILE_KEY_SELECTION]\n                for key in selection_config:\n                    if key not in key_list:\n                        key_list.append(key)\n        logging.debug(\n            f\"For model {model_id}, the following selection keys were in its selection config: {key_list}.\"\n        )\n        return key_list\n\n    def get_selection_values_for_key(self, key: str, model_id: str = None) -> list:\n        \"\"\"Get and return the value of a specified key of the selection dict in the config for a specific `model_id`.\n\n        The key param can contain '.' (dot) separations to allow for retrieval of nested config keys such as\n        'execution.generator.name'\n\n        Parameters\n        ----------\n        key: str\n            The key in the selection dict\n        model_id: str\n            The generative model's unique id\n\n        Returns\n        -------\n        list\n            a list of the values that correspond to the key in the selection config of the `model_id`.\n        \"\"\"\n\n        values_for_key = []\n        if model_id is not None:\n            selection_config = self.get_selection_criteria_by_id(model_id)\n            values_for_key.append(selection_config[key])\n        else:\n            for selection_dict in self.model_selection_dicts:\n                selection_config = selection_dict[CONFIG_FILE_KEY_SELECTION]\n                # if applicable, split key by \".\" and get value in nested dict in selection_config\n                selection_config = Utils.deep_get(base_dict=selection_config, key=key)\n                values_for_key.append(selection_config)\n        logging.debug(\n            f\"For key {key}, the following values were found across the models' selection \"\n            f\"dicts {values_for_key}.\"\n        )\n        return values_for_key\n\n    def get_models_by_key_value_pair(\n        self, key1: str, value1: str, is_case_sensitive: bool = False\n    ) -> list:\n        \"\"\"Get and return a list of `model_id` dicts that contain the specified key value pair in their selection config.\n\n        The key param can contain '.' (dot) separations to allow for retrieval of nested config keys such as\n        'execution.generator.name'. If `key1` points to a list, any value in the list that matches value1` is accepted.\n\n        Parameters\n        ----------\n        key1: str`\n            The key in the selection dict\n        value1: str\n            The value in the selection dict that corresponds to key1\n        is_case_sensitive: bool\n            flag to evaluate keys and values with case sensitivity if set to True\n\n        Returns\n        -------\n        list\n            a list of the dictionaries each containing a model's `model_id` and the found key-value pair in the models config\n        \"\"\"\n\n        model_dict_list = []\n        for selection_dict in self.model_selection_dicts:\n            is_model_match: bool = False\n            # Now, for each model, we want to get the respective value for the key\n            try:\n                key_value = selection_dict[CONFIG_FILE_KEY_SELECTION]\n                key_value = Utils.deep_get(base_dict=key_value, key=key1)\n                if key_value is not None:\n                    # If key value is None, the model is not added to the model\n                    if isinstance(key_value, dict):\n                        # If the value of the key is a dict, we cannot evaluate a dict and continue the loop.\n                        continue\n                    if isinstance(key_value, list):\n                        # If the value of the key is a list, we check if the provided value1 is in that list.\n                        # Convert list of arbitrary type to list of strings\n                        key_value = list(map(str, key_value))\n                        if not is_case_sensitive:\n                            key_value = Utils.list_to_lowercase(key_value)\n                            value1 = value1.lower()\n                        if str(value1) in key_value:\n                            is_model_match = True\n                    else:\n                        # If the value of the key is something else (str, float, int, etc), we check if equal to value1\n                        if (str(key_value) == str(value1)) or (\n                            not is_case_sensitive\n                            and str(key_value).lower() == str(value1).lower()\n                        ):\n                            is_model_match = True\n            except KeyError as e:\n                logging.debug(\n                    f\"Model {selection_dict[MODEL_ID]} was discarded as it does not have the specified keys \"\n                    f\"in its selection dict: {selection_dict}\"\n                )\n                pass\n            if is_model_match:\n                model_id = selection_dict[MODEL_ID]\n                model_dict = {MODEL_ID: model_id, key1: value1}\n                logging.debug(\n                    f\"Model {model_id} was a match for the specified key value pair: {model_dict}\"\n                )\n                model_dict_list.append(model_dict)\n        return model_dict_list\n\n    def rank_models_by_performance(\n        self, model_ids: list = None, metric: str = \"SSIM\", order: str = \"asc\"\n    ) -> list:\n        \"\"\"Rank model based on a provided metric and return sorted list of model dicts.\n\n        The metric param can contain '.' (dot) separations to allow for retrieval via nested metric config keys such as\n        'downstream_task.CLF.accuracy'. If the value found for the metric key is of type list, then the largest value in\n        the list is used for ranking if `order` is descending, while the smallest value is used if `order` is ascending.\n\n        Parameters\n        ----------\n        model_ids: list\n            only evaluate the model_ids in this list. If none, evaluate all available `model_ids`\n        metric: str\n            The key in the selection dict that corresponds to the metric of interest\n        order: str\n            the sorting order of the ranked results. Should be either \"asc\" (ascending) or \"desc\" (descending)\n\n        Returns\n        -------\n        list\n            a list of model dictionaries containing metric and `model_id`, sorted by `metric`.\n        \"\"\"\n\n        model_metric_dict_list = []\n        if model_ids is not None and len(model_ids) == 0:\n            # empty model_ids list -> return empty list.\n            return model_metric_dict_list\n        # First, get all selection criteria for the model_ids\n        selection_dict_list = self.get_selection_criteria_by_ids(\n            model_ids=model_ids, are_model_ids_removed=False\n        )\n        for selection_dict in selection_dict_list:\n            # Now, for each model, we want to get the respective value for the metric\n            try:\n                # Maybe remove the case-sensitivity for metric here.\n                metric_value = selection_dict[CONFIG_FILE_KEY_SELECTION][\n                    CONFIG_FILE_KEY_PERFORMANCE\n                ]\n                metric_value = Utils.deep_get(base_dict=metric_value, key=metric)\n                if metric_value is not None:\n                    # If metric value is None, the model is not added to the model_metric_dict_list\n                    # TODO Maybe add further validation of metric_value here, e.g. string to float conversion, etc.\n                    if isinstance(metric_value, list) and order == \"asc\":\n                        # Assumption: As order is ascending (smallest item at top of list), we want to get the\n                        # smallest (=best) possible value from our metric_value list.\n                        metric_value = min(metric_value)\n                    elif isinstance(metric_value, list):\n                        # Assumption: As order is descending (largest item at top of list), we want to get the\n                        # largest (=best) possible value from our metric_value list.\n                        metric_value = max(metric_value)\n                    model_id = selection_dict[MODEL_ID]\n                    model_metric_dict = {MODEL_ID: model_id, metric: metric_value}\n                    logging.debug(\n                        f\"Model {model_id} was a match for the specified metric value: {model_metric_dict}\"\n                    )\n                    model_metric_dict_list.append(model_metric_dict)\n            except KeyError as e:\n                logging.debug(\n                    f\"Model {selection_dict[MODEL_ID]} was discarded as it does not have the specified keys \"\n                    f\"in its selection dict: {selection_dict}\"\n                )\n                pass\n        if order == \"asc\":\n            # ascending -> the smallest item appears at the top of the list\n            model_metric_dict_list.sort(key=lambda x: x.get(metric))\n        else:\n            # descending -> the largest item appears at the top of the list\n            model_metric_dict_list.sort(key=lambda x: x.get(metric), reverse=True)\n        return model_metric_dict_list\n\n    def find_models_and_rank(\n        self,\n        values: list,\n        target_values_operator: str = \"AND\",\n        are_keys_also_matched: bool = False,\n        is_case_sensitive: bool = False,\n        metric: str = \"SSIM\",\n        order: str = \"asc\",\n    ) -> list:\n        \"\"\"Search for values (and keys) in model configs, rank results and return sorted list of model dicts.\n\n        Parameters\n        ----------\n        values: list\n            list of values used to search and find models corresponding to these `values`\n        target_values_operator: str\n            the operator indicating the relationship between `values` in the evaluation of model search results.\n            Should be either \"AND\", \"OR\", or \"XOR\".\n        are_keys_also_matched: bool\n            flag indicating whether, apart from `values`, the keys in the model config should also be searchable\n        is_case_sensitive: bool\n            flag indicating whether the search for values (and) keys in the model config should be case-sensitive.\n        metric: str\n            The key in the selection dict that corresponds to the `metric` of interest\n        order: str\n            the sorting order of the ranked results. Should be either \"asc\" (ascending) or \"desc\" (descending)\n\n        Returns\n        -------\n        list\n            a list of the searched and matched model dictionaries containing `metric` and `model_id`, sorted by `metric`.\n        \"\"\"\n\n        matching_models = self.find_matching_models_by_values(\n            values=values,\n            target_values_operator=target_values_operator,\n            are_keys_also_matched=are_keys_also_matched,\n            is_case_sensitive=is_case_sensitive,\n        )\n        matching_model_ids = [model.model_id for model in matching_models]\n        logging.debug(f\"matching_model_ids: {matching_model_ids}\")\n        return self.rank_models_by_performance(\n            model_ids=matching_model_ids, metric=metric, order=order\n        )\n\n    def find_matching_models_by_values(\n        self,\n        values: list,\n        target_values_operator: str = \"AND\",\n        are_keys_also_matched: bool = False,\n        is_case_sensitive: bool = False,\n    ) -> list:\n        \"\"\"Search for values (and keys) in model configs and return a list of each matching `ModelMatchCandidate`.\n\n        Uses `self.recursive_search_for_values` to recursively populate each `ModelMatchCandidate` with `MatchedEntry`\n        instances. After populating, each `ModelMatchCandidate` is evaluated based on the provided\n        `target_values_operator` and `values` list using `ModelMatchCandidate.check_if_is_match`.\n\n        Parameters\n        ----------\n        values: list\n            list of values used to search and find models corresponding to these values\n        target_values_operator: str\n            the operator indicating the relationship between `values` in the evaluation of model search results.\n            Should be either \"AND\", \"OR\", or \"XOR\".\n        are_keys_also_matched: bool\n            flag indicating whether, apart from values, the keys in the model config should also be searchable\n        is_case_sensitive: bool\n            flag indicating whether the search for values (and) keys in the model config should be case-sensitive.\n\n        Returns\n        -------\n        list\n            a list of `ModelMatchCandidate` class instances each of which was successfully matched against the search\n            values.\n        \"\"\"\n\n        assert (\n            values is not None and len(values) > 0\n        ), f\"Please specify a list of values to search for. You specified: {values}.\"\n        matching_models = []\n        if not is_case_sensitive:\n            # Removing case-sensitivity search requirement by replacing with lowercase values list\n            values = Utils.list_to_lowercase(target_list=values)\n            logging.debug(f\"Processed search values: {values}\")\n        for selection_dict in self.model_selection_dicts:\n            selection_config = selection_dict[CONFIG_FILE_KEY_SELECTION]\n            model_match_candidate = ModelMatchCandidate(\n                model_id=selection_dict[MODEL_ID],\n                target_values_operator=target_values_operator,\n                is_case_sensitive=is_case_sensitive,\n                target_values=values,\n                are_keys_also_matched=are_keys_also_matched,\n            )\n            model_match_candidate = self.recursive_search_for_values(\n                search_dict=selection_config,\n                model_match_candidate=model_match_candidate,\n            )\n            if model_match_candidate.check_if_is_match():\n                logging.debug(\n                    f\"Found a matching ModelMatchCandidate: {model_match_candidate}\"\n                )\n                matching_models.append(model_match_candidate)\n        return matching_models\n\n    def recursive_search_for_values(\n        self, search_dict: dict, model_match_candidate: ModelMatchCandidate\n    ):\n        \"\"\"Do a recursive search to match values in the `search_dict` with values (and keys) in a model's config.\n\n        The provided `ModelMatchCandidate` instance is recursively populated with `MatchedEntry` instances. A\n        `MatchedEntry` instance contains a key-value pair found in the model's config that matches with one search\n        term of interest.\n\n        The search terms of interest are stored in `ModelMatchCandidate.target_values`. The model's selection config\n        is provided in the 'search_dict'.\n\n        To traverse the `search_dict`, the value for each key in the `search_dict` is retrieved.\n\n        - If that value is of type dictionary, the function calls itself with that nested dictionary as new `search_dict`.\n\n        - If that value is of type list, each value in the list is compared with each search term of interest in `ModelMatchCandidate.target_values`.\n\n        - If the value of the `search_dict` is of another type (i.e. str), it is compared with each search term of interest in `ModelMatchCandidate.target_values`.\n\n        Parameters\n        ----------\n        search_dict: dict\n            contains keys and values from a model's config that are matched against a set of search values.\n        model_match_candidate: ModelMatchCandidate\n            a class instance representing a model to be prepared for evaluation (populated with `MatchedEntry` objects),\n            as to whether it is a match given its search values (`self.target_values`).\n\n        Returns\n        -------\n        list\n            a `ModelMatchCandidate` class instance that has been populated with `MatchedEntry` class instances.\n        \"\"\"\n\n        if search_dict is not None:\n            counter = 0\n            for key in search_dict:\n                if model_match_candidate.are_keys_also_matched and not isinstance(\n                    search_dict, list\n                ):\n                    # Treating the key as a match due to a matching string in target_values.\n                    if (\n                        not model_match_candidate.is_case_sensitive\n                        and key.lower() in model_match_candidate.target_values\n                    ):\n                        matched_entry = MatchedEntry(\n                            key=\"key\", value=key, matching_element=key.lower()\n                        )\n                        model_match_candidate.add_matched_entry(\n                            matched_entry=matched_entry\n                        )\n                    elif key in model_match_candidate.target_values:\n                        matched_entry = MatchedEntry(\n                            key=\"key\", value=key, matching_element=key\n                        )\n                        model_match_candidate.add_matched_entry(\n                            matched_entry=matched_entry\n                        )\n                if isinstance(search_dict, list):\n                    # if we have a list we want the counter to get index position in list\n                    key_or_counter = counter\n                else:\n                    # if we have something else i.e. a dict, we want to get the key to get nested dict\n                    key_or_counter = key\n                if isinstance(search_dict[key_or_counter], dict):\n                    # The value of the key is of type dict, we thus search recursively inside that dictionary\n                    model_match_candidate = self.recursive_search_for_values(\n                        search_dict=search_dict[key_or_counter],\n                        model_match_candidate=model_match_candidate,\n                    )\n                elif isinstance(search_dict[key_or_counter], list):\n                    for item in search_dict[key_or_counter]:\n                        if not model_match_candidate.is_case_sensitive:\n                            item = str(item).lower()\n                        if str(item) in model_match_candidate.target_values:\n                            matched_entry = MatchedEntry(\n                                key=key, value=item, matching_element=str(item)\n                            )\n                            model_match_candidate.add_matched_entry(\n                                matched_entry=matched_entry\n                            )\n                else:\n                    item = search_dict[key_or_counter]\n                    if not model_match_candidate.is_case_sensitive:\n                        item = str(item).lower()\n                    if str(item) in model_match_candidate.target_values:\n                        matched_entry = MatchedEntry(\n                            key=key, value=item, matching_element=str(item)\n                        )\n                        model_match_candidate.add_matched_entry(\n                            matched_entry=matched_entry\n                        )\n                counter += counter\n        return model_match_candidate\n\n    def __repr__(self):\n        return f\"ModelSelector(model_ids={self.config_manager.model_ids})\"\n\n    def __len__(self):\n        raise NotImplementedError\n\n    def __getitem__(self, idx: int):\n        raise NotImplementedError\n"
  },
  {
    "path": "src/medigan/utils.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" `Utils` class providing generalized reusable functions for I/O, parsing, sorting, type conversions, etc. \"\"\"\n# Import python native libs\nimport json\nimport logging\nimport os\nimport shutil\nimport time\nimport zipfile\nfrom distutils.dir_util import copy_tree\nfrom pathlib import Path\nfrom urllib.parse import urlparse  # python3\n\nimport numpy as np\n\n# Import pypi libs\nimport requests\nfrom tqdm import tqdm\n\n\nclass Utils:\n    \"\"\"Utils class containing reusable static methods.\"\"\"\n\n    def __init__(\n        self,\n    ):\n        pass\n\n    @staticmethod\n    def mkdirs(path_as_string: str) -> bool:\n        \"\"\"create folder in `path_as_string` if not already created.\"\"\"\n\n        if not os.path.exists(path_as_string):\n            try:\n                os.makedirs(path_as_string)\n                return True\n            except Exception as e:\n                logging.error(\n                    f\"Error while creating folders for path {path_as_string}: {e}\"\n                )\n                return False\n        return True\n\n    @staticmethod\n    def is_file_located_or_downloaded(\n        path_as_string: str,\n        download_if_not_found: bool = True,\n        download_link: str = None,\n        is_new_download_forced: bool = False,\n        allow_local_path_as_url: bool = True,\n    ) -> bool:\n        \"\"\"check if is file in `path_as_string` and optionally download the file (again).\"\"\"\n\n        if not path_as_string.is_file() or is_new_download_forced:\n            if not download_if_not_found:\n                # download_if_not_found is prioritized over is_new_download_forced in this case, as users likely\n                # prefer to avoid automated downloads altogether when setting download_if_not_found to False.\n                logging.warning(\n                    f\"File {path_as_string} was not found ({not path_as_string.is_file()}) or download \"\n                    f\"was forced ({is_new_download_forced}). However, downloading it from {download_link} \"\n                    f\"was not allowed: download_if_not_found == {download_if_not_found}. This may cause an \"\n                    f\"error, as the file might be outdated or missing, while being used in subsequent \"\n                    f\"workflows.\"\n                )\n                return False\n            else:\n                try:\n                    if allow_local_path_as_url and not Utils.is_url_valid(\n                        the_url=download_link\n                    ):\n                        Utils.copy(\n                            source_path=download_link,\n                            target_path=os.path.split(path_as_string)[0],\n                        )\n                    else:\n                        Utils.download_file(\n                            path_as_string=path_as_string, download_link=download_link\n                        )\n                except Exception as e:\n                    raise e\n        return True\n\n    @staticmethod\n    def download_file(\n        download_link: str, path_as_string: str, file_extension: str = \".json\"\n    ):\n        \"\"\"download a file using the `requests` lib and store in `path_as_string`\"\"\"\n\n        logging.debug(f\"Now downloading file {path_as_string} from {download_link} ...\")\n        try:\n            for i in range(10):\n                response = requests.get(\n                    download_link, allow_redirects=True, stream=True\n                )\n                total_size_in_bytes = int(\n                    response.headers.get(\"content-length\", 0)\n                )  # / (32 * 1024)  # 32*1024 bytes received by requests.\n                logging.debug(total_size_in_bytes)\n                block_size = 1024\n                progress_bar = tqdm(\n                    total=total_size_in_bytes,\n                    unit=\"B\",\n                    unit_scale=True,\n                    position=0,\n                    leave=True,\n                    ascii=True,\n                )\n                progress_bar.set_description(f\"Downloading {download_link}\")\n                with open(path_as_string, \"wb\") as file:\n                    for data in response.iter_content(block_size):\n                        progress_bar.update(len(data))\n                        file.write(data)\n                    logging.debug(\n                        f\"Received response {response}: Retrieved file from {download_link} and wrote it \"\n                        f\"to {path_as_string}.\"\n                    )\n                try:\n                    if not (\n                        download_link.endswith(file_extension)\n                        and Path(path_as_string).is_file()\n                        and str(path_as_string).endswith(file_extension)\n                    ):\n                        # If we do not download a json file (global.json), we assume a zip and want to check if the downloaded zip is valid.\n                        zipfile.ZipFile(path_as_string, \"r\")\n                    break\n                except Exception as e:\n                    print(e)\n                    logging.debug(\n                        f\"Download failed. Retrying download from {download_link}\"\n                    )\n\n        except Exception as e:\n            logging.error(\n                f\"Error while trying to download/copy from {download_link} to {path_as_string}:{e}\"\n            )\n            raise e\n\n    @staticmethod\n    def read_in_json(path_as_string) -> dict:\n        \"\"\"read a .json file and return as dict\"\"\"\n\n        try:\n            with open(path_as_string) as f:\n                json_file = json.load(f)\n                return json_file\n        except Exception as e:\n            logging.error(\n                f\"Error while reading in json file from {path_as_string}: {e}\"\n            )\n            raise e\n\n    @staticmethod\n    def unzip_archive(source_path: Path, target_path: str = \"./\"):\n        \"\"\"unzip a .zip archive in the `target_path`\"\"\"\n\n        try:\n            with zipfile.ZipFile(source_path, \"r\") as zip_ref:\n                zip_ref.extractall(target_path)\n        except Exception as e:\n            logging.error(f\"Error while unzipping {source_path}: {e}\")\n            raise e\n\n    @staticmethod\n    def unzip_and_return_unzipped_path(package_path: str):\n        \"\"\"if not already dir, unzip an archive with `Utils.unzip_archive`. Return path to unzipped dir/file\"\"\"\n\n        if Path(package_path).is_file() and package_path.endswith(\".zip\"):\n            # Get the source_path without .zip extension to unzip.\n            package_path_unzipped = package_path[0:-4]\n            # We have a zip. Let's unzip and do the same operation (with new path)\n            Utils.unzip_archive(\n                source_path=package_path, target_path_as_string=package_path_unzipped\n            )\n            return package_path_unzipped\n        elif Path(package_path).is_dir():\n            logging.info(\n                f\"Your package path ({package_path}) does already point to a directory. It was not unzipped.\"\n            )\n            return package_path\n        else:\n            raise Exception(\n                f\"Your package path ({package_path}) does not point to a zip file nor directory. Please adjust and try again.\"\n            )\n\n    @staticmethod\n    def copy(source_path: Path, target_path: str = \"./\"):\n        \"\"\"copy a folder or file from `source_path` to `target_path`\"\"\"\n\n        try:\n            if Path(source_path).is_file():\n                shutil.copy2(src=source_path, dst=target_path)\n            else:\n                copy_tree(src=source_path, dst=target_path)\n        except Exception as e:\n            logging.error(f\"Error while copying {source_path} to {target_path}: {e}\")\n            raise e\n\n    @staticmethod\n    def dict_to_lowercase(target_dict: dict, string_conversion: bool = True) -> dict:\n        \"\"\"transform values and keys in dict to lowercase, optionally with string conversion of the values.\n\n        Warning: Does not convert nested dicts in the `target_dict`, but rather removes them from return object.\n        \"\"\"\n\n        if string_conversion:\n            # keys should always be strings per default. values might differ in type.\n            return dict((k.lower(), str(v).lower()) for k, v in target_dict.items())\n        else:\n            return dict((k.lower(), v.lower()) for k, v in target_dict.items())\n\n    @staticmethod\n    def list_to_lowercase(target_list: list) -> list:\n        \"\"\"string conversion and lower-casing of values in list.\n\n        trade-off: String conversion for increased robustness > type failure detection\n        \"\"\"\n\n        return [str(x).lower() for x in target_list]\n\n    @staticmethod\n    def deep_get(base_dict: dict, key: str):\n        \"\"\"Split the key by \".\" to get value in nested dictionary.\"\"\"\n        try:\n            key_split = key.split(\".\")\n            for key_ in key_split:\n                base_dict = base_dict[key_]\n            return base_dict\n        except TypeError as e:\n            logging.debug(\n                f\"No key ({key}) found in base_dict ({base_dict}) for this model. Fallback: Returning None.\"\n            )\n        return None\n\n    @staticmethod\n    def is_url_valid(the_url: str) -> bool:\n        \"\"\"Checks if a url is valid using urllib.parse.urlparse\"\"\"\n\n        try:\n            result = urlparse(the_url)\n            # testing if both result.scheme and result.netloc are non-empty strings (empty strings evaluate to False).\n            return all([result.scheme, result.netloc])\n        except Exception:\n            return False\n\n    @staticmethod\n    def has_more_than_n_diff_pixel_values(img: np.ndarray, n: int = 4) -> bool:\n        \"\"\"This function checks whether an image contains more than n different pixel values.\n\n        This helps to differentiate between segmentation masks and actual images.\n        \"\"\"\n\n        import torch\n\n        torch_img = torch.from_numpy(img)\n        pixel_values_set = set(torch_img.flatten().tolist())\n        if len(pixel_values_set) > n:\n            return True\n        else:\n            return False\n\n    @staticmethod\n    def split_images_masks_and_labels(\n        data: list, num_samples: int, max_nested_arrays: int = 2\n    ) -> [list, list, list, list]:\n        \"\"\"Separates the data (sample, mask, other_imaging_data, label) returned by a generative model\n\n        This functions expects a list of tuples as input `data` and assumes that each\n        tuple contains sample, mask, other_imaging_data, label at index positions [0], [1], [2], and [3] respectively.\n\n        samples, masks, and imaging data are expected to be of type np.ndarray and labels of type \"str\".\n\n        For example, this extendable function assumes that, in data, a mask follows the image that it\n        corresponds to or vice versa.\n        \"\"\"\n\n        samples = []\n        masks = []\n        other_imaging_output = []\n        labels = []\n        # if data is smaller than the number of samples that should have been generated, then data likely contains a nested array.\n        # We go a maximum of max_nested_arrays deep into the data.\n        counter = 0\n        while len(data) < num_samples and isinstance(data, list):\n            data = data[0]\n            counter = counter + 1\n            if counter >= max_nested_arrays:\n                break\n\n        for data_point in data:\n            logging.debug(f\"data_point: {data_point}\")\n            if isinstance(data_point, tuple):\n                for i, item in enumerate(data_point):\n                    if isinstance(item, np.ndarray) and i == 0:\n                        samples.append(item)\n                    elif isinstance(item, np.ndarray) and i == 1:\n                        masks.append(item)\n                    elif isinstance(item, np.ndarray) and i == 2:\n                        other_imaging_output.append(item)\n                    elif isinstance(item, str):\n                        labels.append(item)\n            elif isinstance(data_point, np.ndarray):\n                # An image is expected in the case no tuple is returned\n                samples.append(data_point)\n        masks = None if len(masks) == 0 else masks\n        other_imaging_output = (\n            None if len(other_imaging_output) == 0 else other_imaging_output\n        )\n        labels = None if len(labels) == 0 else labels\n        return samples, masks, other_imaging_output, labels\n\n    @staticmethod\n    def split_images_and_masks_no_ordering(\n        data: list, num_samples: int, max_nested_arrays: int = 2\n    ) -> [np.ndarray, np.ndarray]:\n        \"\"\"Extracts and separates the masks from the images if a model returns both in the same np.ndarray.\n\n        This extendable function assumes that, in data, a mask follows the image that it corresponds to or vice versa.\n\n        - This function is deprecated. Please use `split_images_masks_and_labels` instead.\n        \"\"\"\n\n        images = []\n        masks = []\n        # if data is smaller than the number of samples that should have been generated, then data likely contains a nested array.\n        # We go a maximum of max_nested_arrays deep into the data.\n        counter = 0\n        while len(data) < num_samples:\n            data = data[0]\n            counter = counter + 1\n            if counter >= max_nested_arrays:\n                break\n\n        for data_point in data:\n            logging.debug(f\"data_point {data_point}\")\n            if isinstance(data_point, tuple):\n                for i, sample in enumerate(data_point):\n                    if (\n                        isinstance(i, np.ndarray)\n                        and \"int\" in str(i.dtype)\n                        and not Utils.has_more_than_n_diff_pixel_values(i)\n                    ):\n                        # Check if numpy array that contains integers instead of floats indicates the presence of a mask\n                        masks.append(i)\n                    elif Utils.has_more_than_n_diff_pixel_values(i):\n                        images.append(i)\n            elif (\n                isinstance(data_point, np.ndarray)\n                and \"int\" in str(data_point.dtype)\n                and not Utils.has_more_than_n_diff_pixel_values(data_point)\n            ):\n                masks.append(data_point)\n            else:\n                images.append(data_point)\n        masks = None if len(masks) == 0 else masks\n        return images, masks\n\n    @staticmethod\n    def order_dict_by_value(\n        dict_list, key: str, order: str = \"asc\", sort_algorithm=\"bubbleSort\"\n    ) -> list:\n        \"\"\"Sorting a list of dicts by the values of a specific key in the dict using a sorting algorithm.\n\n        - This function is deprecated. You may use Python List sort() with key=lambda function instead.\n\n        \"\"\"\n\n        if sort_algorithm == \"bubbleSort\":\n            for i in range(len(dict_list)):\n                for j in range(len(dict_list) - i - 1):\n                    if dict_list[j][key] > dict_list[j + 1][key]:\n                        # no need for a temp variable holder\n                        dict_list[j][key], dict_list[j + 1][key] = (\n                            dict_list[j + 1][key],\n                            dict_list[j][key],\n                        )\n        return dict_list\n\n    @staticmethod\n    def is_file_in(folder_path: str, filename: str) -> bool:\n        \"\"\"Checks if a file is inside a folder\"\"\"\n\n        try:\n            if (\n                Path(folder_path).is_dir()\n                and Path(f\"{folder_path}/{filename}\").is_file()\n            ):\n                return True\n        except Exception as e:\n            logging.warning(f\"File ({filename}) was not found in {folder_path}: {e}\")\n            return False\n\n    @staticmethod\n    def store_dict_as(\n        dictionary,\n        extension: str = \".json\",\n        output_path: str = \"config/\",\n        filename: str = \"metadata.json\",\n    ):\n        \"\"\"store a Python dictionary in file system as variable filetype.\"\"\"\n\n        if extension not in output_path:\n            Utils.mkdirs(path_as_string=output_path)\n            if extension not in filename:\n                filename = filename + extension\n            output_path = f\"{output_path}/{filename}\"\n        json_object = json.dumps(dictionary, indent=4)\n        with open(output_path, \"w\") as outfile:\n            outfile.write(json_object)\n\n    @staticmethod\n    def call_without_removable_params(\n        my_callable, removable_param_values: list = [None], **params\n    ):\n        \"\"\"call a callable without passing parameters that contain any of the removable_param_values as value.\"\"\"\n\n        not_removed_params = params\n        for removable_param_value in removable_param_values:\n            if removable_param_value is None:\n                not_removed_params = {\n                    k: v\n                    for k, v in not_removed_params.items()\n                    if v is not removable_param_value\n                }\n            else:\n                not_removed_params = {\n                    k: v\n                    for k, v in not_removed_params.items()\n                    if v != removable_param_value\n                }\n        logging.debug(\n            f\"call_without_removable_params final params: {not_removed_params}\"\n        )\n        return my_callable(**not_removed_params)\n\n    def __len__(self):\n        raise NotImplementedError\n\n    def __getitem__(self, idx: int):\n        raise NotImplementedError\n"
  },
  {
    "path": "templates/examples/500.pt.txt",
    "content": "Download 500.pt file from: https://drive.google.com/file/d/1C9vVPymsKJ5i5gpwQM6cpX0y1G89vcpk/view?usp=sharing"
  },
  {
    "path": "templates/examples/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Richard Osuala, Noussair Lazrak\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "templates/examples/__init__.py",
    "content": "\"\"\"\nauthors: Richard Osuala, Zuzanna Szafranowska\nBCN-AIM 2021\n\"\"\"\n\nimport logging\nimport os\nfrom pathlib import Path\n\nimport cv2\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.parallel\n\n\nclass BaseGenerator(nn.Module):\n    def __init__(\n        self,\n        nz: int,\n        ngf: int,\n        nc: int,\n        ngpu: int,\n        leakiness: float = 0.2,\n        bias: bool = False,\n    ):\n        super(BaseGenerator, self).__init__()\n        self.nz = nz\n        self.ngf = ngf\n        self.nc = nc\n        self.ngpu = ngpu\n        self.leakiness = leakiness\n        self.bias = bias\n        self.main = None\n\n    def forward(self, input):\n        raise NotImplementedError\n\n\nclass Generator(BaseGenerator):\n    def __init__(\n        self,\n        nz: int,\n        ngf: int,\n        nc: int,\n        ngpu: int,\n        image_size: int,\n        conditional: bool,\n        leakiness: float,\n        bias: bool = False,\n        n_cond: int = 10,\n        is_condition_categorical: bool = False,\n        num_embedding_dimensions: int = 50,\n    ):\n        super(Generator, self).__init__(\n            nz=nz,\n            ngf=ngf,\n            nc=nc,\n            ngpu=ngpu,\n            leakiness=leakiness,\n            bias=bias,\n        )\n        # if is_condition_categorical is False, we model the condition as continous input to the network\n        self.is_condition_categorical = is_condition_categorical\n\n        # n_cond is only used if is_condition_categorical is True.\n        self.num_embedding_input = n_cond\n\n        # num_embedding_dimensions is only used if is_condition_categorical is True.\n        # num_embedding_dimensions standard would be dim(z), but atm we have a nn.Linear after\n        # nn.Embedding that upscales the dimension to self.nz. Using same value of num_embedding_dims in D and G.\n        self.num_embedding_dimensions = num_embedding_dimensions\n\n        # whether the is a conditional input into the GAN i.e. cGAN\n        self.conditional: bool = conditional\n\n        # The image size (supported params should be 128 or 64)\n        self.image_size = image_size\n\n        if self.image_size == 128:\n            self.first_layers = nn.Sequential(\n                # input is Z, going into a convolution\n                nn.ConvTranspose2d(\n                    self.nz * self.nc, self.ngf * 16, 4, 1, 0, bias=self.bias\n                ),\n                nn.BatchNorm2d(self.ngf * 16),\n                nn.ReLU(True),\n                # state size. (ngf*16) x 4 x 4\n                nn.ConvTranspose2d(\n                    self.ngf * 16, self.ngf * 8, 4, 2, 1, bias=self.bias\n                ),\n                nn.BatchNorm2d(self.ngf * 8),\n                nn.ReLU(True),\n            )\n        elif self.image_size == 64:\n            self.first_layers = nn.Sequential(\n                # input is Z, going into a convolution\n                nn.ConvTranspose2d(\n                    self.nz * self.nc, self.ngf * 8, 4, 1, 0, bias=self.bias\n                ),\n                nn.BatchNorm2d(self.ngf * 8),\n                nn.ReLU(True),\n            )\n        else:\n            raise ValueError(\n                f\"Allowed image sizes are 128 and 64. You provided {self.image_size}. Please adjust.\"\n            )\n\n        self.main = nn.Sequential(\n            *self.first_layers.children(),\n            # state size. (ngf*8) x 8 x 8\n            nn.ConvTranspose2d(self.ngf * 8, self.ngf * 4, 4, 2, 1, bias=self.bias),\n            nn.BatchNorm2d(self.ngf * 4),\n            nn.ReLU(True),\n            # state size. (ngf*4) x 16 x 16\n            nn.ConvTranspose2d(self.ngf * 4, self.ngf * 2, 4, 2, 1, bias=self.bias),\n            nn.BatchNorm2d(self.ngf * 2),\n            nn.ReLU(True),\n            # state size. (ngf*2) x 32 x 32\n            nn.ConvTranspose2d(self.ngf * 2, self.ngf, 4, 2, 1, bias=self.bias),\n            nn.BatchNorm2d(self.ngf),\n            nn.ReLU(True),\n            # state size. (ngf) x 64 x 64\n            # Note that out_channels=1 instead of out_channels=self.nc.\n            # This is due to conditional input channel of our grayscale images\n            nn.ConvTranspose2d(\n                in_channels=self.ngf,\n                out_channels=1,\n                kernel_size=4,\n                stride=2,\n                padding=1,\n                bias=self.bias,\n            ),\n            nn.Tanh(),\n            # state size. (nc) x 128 x 128\n        )\n\n        if self.is_condition_categorical:\n            self.embed_nn = nn.Sequential(\n                # e.g. condition -> int -> embedding -> fcl -> feature map -> concat with image -> conv layers..\n                # embedding layer\n                nn.Embedding(\n                    num_embeddings=self.num_embedding_input,\n                    embedding_dim=self.num_embedding_dimensions,\n                ),\n                # target output dim of dense layer is batch_size x self.nz x 1 x 1\n                # input is dimension of the embedding layer output\n                nn.Linear(\n                    in_features=self.num_embedding_dimensions, out_features=self.nz\n                ),\n                # nn.BatchNorm1d(self.nz),\n                nn.LeakyReLU(self.leakiness, inplace=True),\n            )\n        else:\n            self.embed_nn = nn.Sequential(\n                # target output dim of dense layer is: nz x 1 x 1\n                # input is dimension of the numbers of embedding\n                nn.Linear(in_features=1, out_features=self.nz),\n                # TODO Ablation: How does BatchNorm1d affect the conditional model performance?\n                nn.BatchNorm1d(self.nz),\n                nn.LeakyReLU(self.leakiness, inplace=True),\n            )\n\n    def forward(self, x, conditions=None):\n        if self.conditional:\n            # combining condition labels and input images via a new image channel\n            if not self.is_condition_categorical:\n                # If labels are continuous (not modelled as categorical), use floats instead of integers for labels.\n                # Also adjust dimensions to (batch_size x 1) as needed for input into linear layer\n                # labels should already be of type float, no change expected in .float() conversion (it is only a safety check)\n\n                # Just for testing:\n                conditions *= 0\n                conditions += 1\n\n                conditions = conditions.view(conditions.size(0), -1).float()\n            embedded_conditions = self.embed_nn(conditions)\n            embedded_conditions_with_random_noise_dim = embedded_conditions.view(\n                -1, self.nz, 1, 1\n            )\n            x = torch.cat([x, embedded_conditions_with_random_noise_dim], 1)\n        return self.main(x)\n\n\ndef interval_mapping(image, from_min, from_max, to_min, to_max):\n    # map values from [from_min, from_max] to [to_min, to_max]\n    # image: input array\n    from_range = from_max - from_min\n    to_range = to_max - to_min\n    # scale to interval [0,1]\n    scaled = np.array((image - from_min) / float(from_range), dtype=float)\n    # multiply by range and add minimum to get interval [min,range+min]\n    return to_min + (scaled * to_range)\n\n\ndef image_generator(model_path, device, nz, ngf, nc, ngpu, num_samples):\n    # instantiate the model\n    logging.debug(\"Instantiating model...\")\n    netG = Generator(\n        nz=nz,\n        ngf=ngf,\n        nc=nc,\n        ngpu=ngpu,\n        image_size=128,\n        leakiness=0.1,\n        conditional=False,\n    )\n    if device.type == \"cuda\":\n        netG.cuda()\n\n    # load the model's weights from state_dict *'.pt file\n    logging.debug(f\"Loading model weights from {model_path} ...\")\n\n    checkpoint = torch.load(model_path, map_location=device)\n    try:\n        netG.load_state_dict(state_dict=checkpoint[\"generator\"])\n    except KeyError:\n        raise KeyError(\n            f\"checkpoint['generator_state_dict'] was not found.\"\n        )  # checkpoint={checkpoint}\")\n    logging.debug(f\"Using retrieved model from generator_state_dict checkpoint\")\n    netG.eval()\n\n    # generate the images\n    logging.debug(f\"Generating {num_samples} images using {device}...\")\n    z = torch.randn(num_samples, nz, 1, 1, device=device)\n    images = netG(z).detach().cpu().numpy()\n    image_list = []\n    for j, img_ in enumerate(images):\n        image_list.append(img_)\n    return image_list\n\n\ndef save_generated_images(image_list, path):\n    logging.debug(f\"Saving generated images now in {path}\")\n    for i, img_ in enumerate(image_list):\n        Path(path).mkdir(parents=True, exist_ok=True)\n        img_path = f\"{path}/{i}.png\"\n        img_ = interval_mapping(img_.transpose(1, 2, 0), -1.0, 0.0, 0, 255)\n        img_ = img_.astype(\"uint8\")\n        cv2.imwrite(img_path, img_)\n    logging.debug(f\"Saved generated images to {path}\")\n\n\ndef return_images(image_list):\n    # logging.debug(f\"Returning generated images as {type(image_list)}.\")\n    processed_image_list = []\n    for i, img_ in enumerate(image_list):\n        img_ = interval_mapping(img_.transpose(1, 2, 0), -1.0, 0.0, 0, 255)\n        img_ = img_.astype(\"uint8\")\n        processed_image_list.append(img_)\n    return processed_image_list\n\n\ndef generate(model_file, num_samples, output_path, save_images: bool):\n    \"\"\"This function generates synthetic images of mammography regions of interest\"\"\"\n    try:\n        device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n        ngpu = 0\n        if device == \"cuda\":\n            ngpu = 1\n        image_list = image_generator(model_file, device, 100, 64, 1, ngpu, num_samples)\n        if save_images:\n            save_generated_images(image_list, output_path)\n        else:\n            return return_images(image_list)\n    except Exception as e:\n        logging.error(\n            f\"Error while trying to generate {num_samples} images with model {model_file}: {e}\"\n        )\n        raise e\n"
  },
  {
    "path": "templates/examples/metadata.json",
    "content": "{\n  \"00005_DCGAN_MMG_MASS_ROI\": {\n    \"execution\": {\n      \"package_name\": \"MMG_MASS_BCDR_DCGAN\",\n      \"package_link\": \"ADD_ZENODO_OR_LOCAL_URL_HERE\",\n      \"model_name\": \"500\",\n      \"extension\": \".pt\",\n      \"image_size\": [\n        128,\n        128\n      ],\n      \"dependencies\": [\n        \"numpy\",\n        \"torch\",\n        \"opencv-contrib-python-headless\"\n      ],\n      \"generate_method\": {\n        \"name\": \"generate\",\n        \"args\": {\n          \"base\": [\n            \"model_file\",\n            \"num_samples\",\n            \"output_path\",\n            \"save_images\"\n          ],\n          \"custom\": {}\n        }\n      }\n    },\n    \"selection\": {\n      \"performance\": {\n        \"SSIM\": null,\n        \"MSE\": null,\n        \"NSME\": null,\n        \"PSNR\": null,\n        \"IS\": null,\n        \"turing_test\": null,\n        \"FID_no_images\":1000,\n        \"FID\": 67.60,\n        \"FID_ratio\": 0.497,\n        \"FID_RADIMAGENET\": 1.27,\n        \"FID_RADIMAGENET_ratio\": 0.197,\n        \"CLF_delta\": null,\n        \"SEG_delta\": null,\n        \"CLF\": {\n          \"trained_on_fake\": {\n            \"accuracy\": 0.9528,\n            \"f1\": 0.9721,\n            \"AUROC\": 0.9596,\n            \"AUPRC\": 0.9908\n          },\n          \"trained_on_real_and_fake\": {},\n          \"trained_on_real\": {}\n        },\n        \"SEG\": {\n                \"trained_on_fake\": {},\n                \"trained_on_real_and_fake\": {},\n                \"trained_on_real\": {}\n        }\n      },\n      \"use_cases\": [\n        \"classification\"\n      ],\n      \"organ\": [\n        \"breast\",\n        \"breasts\",\n        \"chest\"\n      ],\n      \"modality\": [\n        \"MMG\",\n        \"Mammography\",\n        \"Mammogram\",\n        \"full-field digital\",\n        \"full-field digital MMG\",\n        \"full-field MMG\",\n        \"full-field Mammography\",\n        \"digital Mammography\",\n        \"digital MMG\",\n        \"x-ray mammography\"\n      ],\n      \"vendors\": [],\n      \"centres\": [],\n      \"function\": [\n        \"noise to image\",\n        \"image generation\",\n        \"unconditional generation\",\n        \"data augmentation\"\n      ],\n      \"condition\": [],\n      \"dataset\": [\n        \"BCDR\"\n      ],\n      \"augmentations\": [\n        \"horizontal flip\",\n        \"vertical flip\"\n      ],\n      \"generates\": [\n        \"mass\",\n        \"masses\",\n        \"mass roi\",\n        \"mass ROI\",\n        \"mass images\",\n        \"mass region of interest\",\n        \"nodule\",\n        \"nodule\",\n        \"nodule roi\",\n        \"nodule ROI\",\n        \"nodule images\",\n        \"nodule region of interest\"\n      ],\n      \"height\": 128,\n      \"width\": 128,\n      \"depth\": null,\n      \"type\": \"DCGAN\",\n      \"license\": \"MIT\",\n      \"dataset_type\": \"public\",\n      \"privacy_preservation\": null,\n      \"tags\": [\n        \"Mammogram\",\n        \"Mammography\",\n        \"Digital Mammography\",\n        \"Full field Mammography\",\n        \"Full-field Mammography\",\n        \"128x128\",\n        \"128 x 128\",\n        \"MammoGANs\",\n        \"Masses\",\n        \"Nodules\"\n      ],\n      \"year\": \"2021\"\n    },\n    \"description\": {\n      \"title\": \"DCGAN Model for Mammogram MASS Patch Generation (Trained on BCDR)\",\n      \"provided_date\": \"15 Dec 2021\",\n      \"trained_date\": \"Nov 2021\",\n      \"provided_after_epoch\": 1500,\n      \"version\": \"0.0.1\",\n      \"publication\": null,\n      \"doi\": [],\n      \"comment\": \"A deep convolutional generative adversarial network (DCGAN) that generates mass patches of mammograms. Pixel dimensions are 128x128. The DCGAN was trained on MMG patches from the BCDR dataset (Lopez et al, 2012). The uploaded ZIP file contains the files 500.pt (model weight), __init__.py (image generation method and utils), a requirements.txt, and the GAN model architecture (in pytorch) below the /src folder.\"\n    }\n  },"
  },
  {
    "path": "templates/examples/requirements.txt",
    "content": "numpy\ntorch\nopencv-contrib-python-headless"
  },
  {
    "path": "templates/examples/test.sh",
    "content": "#! /bin/bash\n\necho \"Running a test: Generate method of this medigan model module.\"\n\necho \"If not done already, please download 500.pt file from: https://drive.google.com/file/d/1C9vVPymsKJ5i5gpwQM6cpX0y1G89vcpk/view?usp=sharing\"\n\necho \"1. Creating and activating virtual environment called MMG_env.\"\npython3 -m venv MMG_env\nsource MMG_env/bin/activate\n\necho \"2. Pip install dependencies from requirements.txt\"\npip install -r requirements.txt\n\necho \"3. Run the generate function with parameters\"\npython __init__.py \n\npython -c \"from __init__ import generate; \nmodel_file='500.pt';\nnum_samples=10;\noutput_path='images/';\nsave_images=True;\ngenerate(model_file=model_file, num_samples=num_samples, output_path=output_path, save_images=save_images)\"\n\necho \"4. Done. Any errors? Have synthetic images been successfully created in folder /images?\""
  },
  {
    "path": "templates/raw_examples/LICENSE",
    "content": "copy license here\n"
  },
  {
    "path": "templates/raw_examples/__init__.py",
    "content": ""
  },
  {
    "path": "templates/raw_examples/metadata.json",
    "content": "{\n  \"MODEL_ID\": {\n    \"execution\": {\n      \"package_name\": null,\n      \"package_link\": null,\n      \"model_name\": null,\n      \"extension\": null,\n      \"image_size\": [],\n      \"dependencies\": [],\n      \"generate_method\": {\n        \"name\": null,\n        \"args\": {\n          \"base\": [\n            \"model_file\",\n            \"num_samples\",\n            \"output_path\",\n            \"save_images\"\n          ],\n          \"custom\": {}\n        }\n      }\n    },\n    \"selection\": {\n      \"performance\": {\n        \"SSIM\": null,\n        \"MSE\": null,\n        \"NSME\": null,\n        \"PSNR\": null,\n        \"IS\": null,\n        \"FID\": null,\n        \"turing_test\": null,\n        \"downstream_task\": {\n          \"CLF\": {\n            \"trained_on_fake\": {\n              \"accuracy\": null,\n              \"precision\": null,\n              \"recall\": null,\n              \"f1\": null,\n              \"specificity\": null,\n              \"AUROC\": null,\n              \"AUPRC\": null\n            },\n            \"trained_on_real_and_fake\": {},\n            \"trained_on_real\": {}\n          },\n          \"SEG\": {\n            \"trained_on_fake\": {\n              \"dice\": null,\n              \"jaccard\": null,\n              \"accuracy\": null,\n              \"precision\": null,\n              \"recall\": null,\n              \"f1\": null\n            },\n            \"trained_on_real_and_fake\": {},\n            \"trained_on_real\": {}\n          }\n        }\n      },\n      \"use_cases\": [],\n      \"organ\": [],\n      \"modality\": [],\n      \"vendors\": [],\n      \"centres\": [],\n      \"function\": [],\n      \"condition\": [],\n      \"dataset\": [],\n      \"augmentations\": [],\n      \"generates\": [],\n      \"height\": null,\n      \"width\": null,\n      \"depth\": null,\n      \"type\": null,\n      \"license\": null,\n      \"dataset_type\": null,\n      \"privacy_preservation\": null,\n      \"tags\": [],\n      \"year\": null\n    },\n    \"description\": {\n      \"title\": null,\n      \"provided_date\": null,\n      \"trained_date\": null,\n      \"provided_after_epoch\": null,\n      \"version\": null,\n      \"publication\": null,\n      \"doi\": [],\n      \"comment\": null\n    }\n  },\n}\n"
  },
  {
    "path": "templates/raw_examples/model.pt",
    "content": ""
  },
  {
    "path": "templates/template.json",
    "content": "{\n  \"MODEL_ID\": {\n    \"execution\": {\n      \"package_name\": \"\",\n      \"package_link\": \"\",\n      \"model_name\": \"\",\n      \"extension\": \"\",\n      \"image_size\": [],\n      \"dependencies\": [],\n      \"generate_method\": {\n        \"name\": \"\",\n        \"args\": {\n          \"base\": [\n            \"model_file\",\n            \"num_samples\",\n            \"output_path\",\n            \"save_images\"\n          ],\n          \"custom\": {}\n        }\n      }\n    },\n    \"selection\": {\n      \"performance\": {\n        \"SSIM\": null,\n        \"MSE\": null,\n        \"NSME\": null,\n        \"PSNR\": null,\n        \"IS\": null,\n        \"FID\": null,\n        \"turing_test\": \"\",\n        \"downstream_task\": {\n          \"CLF\": {\n            \"trained_on_fake\": {\n              \"accuracy\": null,\n              \"precision\": null,\n              \"recall\": null,\n              \"f1\": null,\n              \"specificity\": null,\n              \"AUROC\": null,\n              \"AUPRC\": null\n            },\n            \"trained_on_real_and_fake\": {},\n            \"trained_on_real\": {}\n          },\n          \"SEG\": {\n            \"trained_on_fake\": {\n              \"dice\": null,\n              \"jaccard\": null,\n              \"accuracy\": null,\n              \"precision\": null,\n              \"recall\": null,\n              \"f1\": null\n            },\n            \"trained_on_real_and_fake\": {},\n            \"trained_on_real\": {}\n          }\n        }\n      },\n      \"use_cases\": [],\n      \"organ\": [],\n      \"modality\": [],\n      \"vendors\": [],\n      \"centres\": [],\n      \"function\": [],\n      \"condition\": [],\n      \"dataset\": [],\n      \"augmentations\": [],\n      \"generates\": [],\n      \"height\": null,\n      \"width\": null,\n      \"depth\": null,\n      \"type\": \"\",\n      \"license\": \"\",\n      \"dataset_type\": \"\",\n      \"privacy_preservation\": \"\",\n      \"tags\": [],\n      \"year\": null\n    },\n    \"description\": {\n      \"title\": \"\",\n      \"provided_date\": \"\",\n      \"trained_date\": \"\",\n      \"provided_after_epoch\": null,\n      \"version\": \"\",\n      \"publication\": \"\",\n      \"doi\": [],\n      \"inputs\": [],\n      \"comment\": \"\"\n    }\n  }\n}\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/fid.py",
    "content": "\"\"\"\nCalculates the Frechet Inception Distance between two distributions, using chosen feature extractor model.\n\nRadImageNet Model source: https://github.com/BMEII-AI/RadImageNet\nRadImageNet InceptionV3 weights (original, broken since 11.07.2023): https://drive.google.com/file/d/1p0q9AhG3rufIaaUE1jc2okpS8sdwN6PU\nRadImageNet InceptionV3 weights (for medigan, updated link 11.07.2023): https://drive.google.com/drive/folders/1lGFiS8_a5y28l4f8zpc7fklwzPJC-gZv\n\nUsage:\n    python fid.py dir1 dir2 \n\"\"\"\n\nimport argparse\nimport os\n\nimport cv2\nimport numpy as np\nimport tensorflow as tf\nimport tensorflow_gan as tfgan\nimport wget\nfrom tensorflow.keras.applications import InceptionV3\nfrom tensorflow.keras.applications.inception_v3 import preprocess_input\n\nimg_size = 299\nbatch_size = 64\nnum_batches = 1\nRADIMAGENET_URL = \"https://drive.google.com/uc?id=1uvJHLG1K71Qzl7Km4JMpNOwE7iTjN8g9\"\nRADIMAGENET_WEIGHTS = \"RadImageNet-InceptionV3_notop.h5\"\nIMAGENET_TFHUB_URL = \"https://tfhub.dev/tensorflow/tfgan/eval/inception/1\"\n\n\ndef parse_args() -> argparse.Namespace:\n    parser = argparse.ArgumentParser(\n        description=\"Calculates the Frechet Inception Distance between two distributions using RadImageNet model.\"\n    )\n    parser.add_argument(\n        \"dataset_path_1\",\n        type=str,\n        help=\"Path to images from first dataset\",\n    )\n    parser.add_argument(\n        \"dataset_path_2\",\n        type=str,\n        help=\"Path to images from second dataset\",\n    )\n    parser.add_argument(\n        \"--model\",\n        type=str,\n        default=\"imagenet\",\n        help=\"Use RadImageNet feature extractor for FID calculation\",\n    )\n    parser.add_argument(\n        \"--lower_bound\",\n        action=\"store_true\",\n        help=\"Calculate lower bound of FID using the 50/50 split of images from dataset_path_1\",\n    )\n    parser.add_argument(\n        \"--normalize_images\",\n        action=\"store_true\",\n        help=\"Normalize images from both datasources using min and max of each sample\",\n    )\n    args = parser.parse_args()\n    return args\n\n\ndef load_images(directory, normalize=False, split=False, limit=None):\n    \"\"\"\n    Loads images from the given directory.\n    If split is True, then half of the images is loaded to one array and the other half to another.\n    \"\"\"\n    if split:\n        subset_1 = []\n        subset_2 = []\n    else:\n        images = []\n\n    for count, filename in enumerate(os.listdir(directory)):\n        if filename.lower().endswith((\".png\", \".jpg\", \".jpeg\")):\n            img = cv2.imread(os.path.join(directory, filename))\n            img = cv2.resize(img, (img_size, img_size), interpolation=cv2.INTER_LINEAR)\n            if normalize:\n                img = cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX)\n            if len(img.shape) > 2 and img.shape[2] == 4:\n                img = img[:, :, :3]\n            if len(img.shape) == 2:\n                img = np.stack([img] * 3, axis=2)\n\n            if split:\n                if count % 2 == 0:\n                    subset_1.append(img)\n                else:\n                    subset_2.append(img)\n            else:\n                images.append(img)\n        if count == limit:\n            break\n    if split:\n        subset_1 = preprocess_input(np.array(subset_1))\n        subset_2 = preprocess_input(np.array(subset_2))\n        return subset_1, subset_2\n    else:\n        images = preprocess_input(np.array(images))\n        return images\n\n\ndef check_model_weights(model_name):\n    \"\"\"\n    Checks if the model weights are available and download them if not.\n    \"\"\"\n    model_weights_path = None\n    if model_name == \"radimagenet\":\n        model_weights_path = RADIMAGENET_WEIGHTS\n        if not os.path.exists(RADIMAGENET_WEIGHTS):\n            print(\"Downloading RadImageNet InceptionV3 model:\")\n            wget.download(\n                RADIMAGENET_URL,\n                model_weights_path,\n            )\n            print(\"\\n\")\n        return model_weights_path\n\n\ndef _radimagenet_fn(images):\n    \"\"\"\n    Get RadImageNet inception v3 model\n    \"\"\"\n    model = InceptionV3(\n        weights=RADIMAGENET_WEIGHTS,\n        input_shape=(img_size, img_size, 3),\n        include_top=False,\n        pooling=\"avg\",\n    )\n    output = model(images)\n    output = tf.nest.map_structure(tf.keras.layers.Flatten(), output)\n    return output\n\n\ndef get_classifier_fn(model_name=\"imagenet\"):\n    \"\"\"\n    Get model as TF function for optimized inference.\n    \"\"\"\n    check_model_weights(model_name)\n\n    if model_name == \"radimagenet\":\n        return _radimagenet_fn\n    elif model_name == \"imagenet\":\n        return tfgan.eval.classifier_fn_from_tfhub(IMAGENET_TFHUB_URL, \"pool_3\", True)\n    else:\n        raise ValueError(\"Model {} not recognized\".format(model_name))\n\n\ndef calculate_fid(\n    directory_1,\n    directory_2,\n    model_name,\n    lower_bound=False,\n    normalize_images=False,\n):\n    \"\"\"\n    Calculates the Frechet Inception Distance between two distributions using chosen feature extractor model.\n    \"\"\"\n    limit = min(len(os.listdir(directory_1)), len(os.listdir(directory_2)))\n    if lower_bound:\n        images_1, images_2 = load_images(directory_1, split=True, limit=limit)\n    else:\n        images_1 = load_images(directory_1, limit=limit, normalize=normalize_images)\n        images_2 = load_images(directory_2, limit=limit, normalize=normalize_images)\n\n    fid = tfgan.eval.frechet_classifier_distance(\n        images_1, images_2, get_classifier_fn(model_name)\n    )\n\n    return fid\n\n\nif __name__ == \"__main__\":\n    args = parse_args()\n\n    directory_1 = args.dataset_path_1\n    directory_2 = args.dataset_path_2\n    lower_bound = args.lower_bound\n    normalize_images = args.normalize_images\n    model_name = args.model\n\n    fid = calculate_fid(\n        directory_1=directory_1,\n        directory_2=directory_2,\n        model_name=model_name,\n        lower_bound=lower_bound,\n        normalize_images=normalize_images,\n    )\n\n    if lower_bound:\n        print(\"Lower bound FID {}: {}\".format(model_name, fid))\n    else:\n        print(\"FID {}: {}\".format(model_name, fid))\n"
  },
  {
    "path": "tests/model_contribution_test_manual.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" script for quick local testing if a new model can be added and works inside medigan.\"\"\"\n# run with python -m tests.model_contribution_test_manual\n\nimport glob\nimport logging\nimport shutil\nimport sys\nimport unittest\n\ntry:\n    from src.medigan.generators import Generators\n\n    LOGGING_LEVEL = \"INFO\"\n    logger = logging.getLogger()  # (__name__)\n    logger.setLevel(LOGGING_LEVEL)\n    stream_handler = logging.StreamHandler(sys.stdout)\n    stream_handler.setLevel(LOGGING_LEVEL)\n    formatter = logging.Formatter(\n        \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n    )\n    stream_handler.setFormatter(formatter)\n    logger.addHandler(stream_handler)\n\n    generators = Generators()\n\n    # Testing init of contributor with correct params\n    init_py_path = \"../models/00012_C-DCGAN_MMG_MASSES/__init__.py\"\n    metadata_file_path = \"../models/00012_C-DCGAN_MMG_MASSES/metadata.json\"\n    model_id = \"00012_C-DCGAN_MMG_MASSES\"\n\n    zenodo_access_token = \"ACCESS_TOKEN\"\n    github_access_token = \"ACCESS_TOKEN\"\n\n    creator_name = \"John Doe\"\n    creator_affiliation = \"University of Barcelona\"\n\n    # Testing full model contribution workflow.\n    generators.contribute(\n        model_id=model_id,\n        init_py_path=init_py_path,\n        zenodo_access_token=zenodo_access_token,\n        github_access_token=github_access_token,\n        metadata_file_path=metadata_file_path,\n        creator_name=creator_name,\n        creator_affiliation=creator_affiliation,\n    )\n\n    # Testing init of contributor with erroneous params\n    # contributor = generators.add_model_contributor(model_id ='Some model id', init_py_path=\"somePath\")\n    # contributor = generators.add_model_contributor(model_id ='00008_WGANGP_MMG_MASS_ROI', init_py_path=\"somePath\")\n    # contributor = generators.add_model_contributor(model_id ='Some model id', init_py_path=\"init_py_path\")\n\n    # Creating the model contributor\n    # generators.add_model_contributor(model_id=model_id, init_py_path=init_py_path)\n\n    # Adding the metadata of the model from input\n    # generators.add_metadata_from_file(\n    #    model_id=model_id, metadata_file_path=metadata_file_path\n    # )\n\n    #  Alternatively, Adding the metadata of the model from file\n    # metadata = contributor.add_metadata_from_input(\n    #                                               model_weights_name = \"10000\",\n    #                                               model_weights_extension=\".pt\",\n    #                                               generate_method_name = \"generate\",\n    #                                               dependencies=[\"numpy\", \"torch\", \"opencv-contrib-python-headless\"])\n\n    # Add metadata to global.json config\n    # generators.test_model(model_id=model_id)\n\n    # Alternatively, explicitely providing model metadata to add the metadata to config\n    # generators._add_model_to_config(model_id=model_id, metadata=metadata, metadata_file_path=metadata_file_path,\n    #                               overwrite_existing_metadata=True)\n\n    # Zenodo upload test\n    # generators.push_to_zenodo(\n    #    model_id=model_id,\n    #    access_token=zenodo_access_token,\n    #    creator_name=\"test\",\n    #    creator_affiliation=\"test affiliation\",\n    # )\n\n    # Manual Zenodo Test 1\n    # import requests\n    # r = requests.get('https://zenodo.org/api/deposit/depositions', params = {'access_token': zenodo_access_token})\n    # print(r.status_code)\n    # print(r.json())\n\n    # Manual Zenodo Test 2\n    # headers = {\"Content-Type\": \"application/json\"}\n    # params = {\"access_token\": zenodo_access_token}\n    # r = requests.post(\n    #    \"https://zenodo.org/api/deposit/depositions\",\n    #    params=params,\n    #    json={},\n    #    headers=headers,\n    # )\n    # print(r.json())\n    # print(r.status_code)\n\n    # Github upload test\n    # generators.push_to_github(\n    #    model_id=model_id,\n    #    github_access_token=github_access_token,\n    #    package_link=None,\n    #    creator_name=\"test\",\n    #    creator_affiliation=\"test affiliation\",\n    #    model_description=\"test description\",\n    # )\n\nexcept Exception as e:\n    logging.error(f\"test_init_generators error: {e}\")\n    raise e\n"
  },
  {
    "path": "tests/model_integration_test_manual.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" script for quick local testing if a model works inside medigan.\"\"\"\n# run with python -m tests.model_integration_test_manual\n\nimport logging\n\nMODEL_ID = \"YOUR_MODEL_ID_HERE\"\nMODEL_ID = 23  # \"00023_PIX2PIXHD_BREAST_DCEMRI\" #\"00002_DCGAN_MMG_MASS_ROI\"  # \"00007_BEZIERCURVE_TUMOUR_MASK\"\nNUM_SAMPLES = 2\nOUTPUT_PATH = f\"output/{MODEL_ID}/\"\ntry:\n    from src.medigan.generators import Generators\n\n    generators = Generators()\nexcept Exception as e:\n    logging.error(f\"test_init_generators error: {e}\")\n    raise e\n\ngenerators.generate(\n    model_id=MODEL_ID,\n    num_samples=NUM_SAMPLES,\n    output_path=OUTPUT_PATH,\n    input_path=\"input/\",\n    gpu_id=0,\n    image_size=448,\n    install_dependencies=True,\n)\n\ndata_loader = generators.get_as_torch_dataloader(\n    model_id=MODEL_ID,\n    num_samples=NUM_SAMPLES,\n    output_path=OUTPUT_PATH,\n    input_path=\"input/\",\n    gpu_id=0,\n    image_size=448,\n    # prefetch_factor=2, # debugging with torch v2.0.0: This will raise an error for torch DataLoader if num_workers == None at the same time.\n)\n\nprint(f\"len(data_loader): {len(data_loader)}\")\n\nif len(data_loader) != NUM_SAMPLES:\n    logging.warning(\n        f\"{MODEL_ID}: The number of samples in the dataloader (={len(data_loader)}) is not equal the number of samples requested (={NUM_SAMPLES}).\"\n    )\n\n#### Get the object at index 0 from the dataloader\ndata_dict = next(iter(data_loader))\n\nprint(f\"data_dict: {data_dict}\")\n"
  },
  {
    "path": "tests/test_model_executor.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" main test script to test the primary functions/classes/methods. \"\"\"\n# run with python -m tests.test_generator\n\nimport glob\nimport logging\nimport os\nimport shutil\nimport sys\n\nimport pytest\nimport torch\n\n# import unittest\n\n\n# Set the logging level depending on the level of detail you would like to have in the logs while running the tests.\nLOGGING_LEVEL = logging.INFO  # WARNING  # logging.INFO\n\nmodels_with_args = [\n    (\n        \"00001_DCGAN_MMG_CALC_ROI\",\n        {},\n        100,\n    ),  # 100 samples to test automatic batch-wise image generation in model_executor\n    (\n        \"00002\",\n        {},\n        3,\n    ),  # \"00002\" instead of \"00002_DCGAN_MMG_MASS_ROI\" to test shortcut model_ids\n    (\n        \"03\",\n        {\"translate_all_images\": False},\n        2,\n    ),  # \"03\" instead of \"00003_CYCLEGAN_MMG_DENSITY_FULL\" to test shortcut model_ids\n    (\n        4,  # 4 instead of \"00004_PIX2PIX_MMG_MASSES_W_MASKS\" to test shortcut model_ids\n        {\n            \"shapes\": [\"oval\"],\n            \"ssim_threshold\": 0.18,\n            \"image_size\": [128, 128],\n            \"patch_size\": [30, 30],\n        },\n        3,\n    ),\n    (\"00005_DCGAN_MMG_MASS_ROI\", {}, 3),\n    (\"00006_WGANGP_MMG_MASS_ROI\", {}, 3),\n    (\n        \"00007_INPAINT_BRAIN_MRI\",\n        {\n            \"image_size\": (256, 256),\n            \"num_inpaints_per_sample\": 2,\n            \"randomize_input_image_order\": False,\n            \"add_variations_to_mask\": False,\n            \"x_center\": 120,\n            \"y_center\": 140,\n            \"radius_1\": 8,\n            \"radius_2\": 12,\n            \"radius_3\": 24,\n        },\n        3,\n    ),\n    (\n        \"00008_C-DCGAN_MMG_MASSES\",\n        {\"condition\": 0, \"is_cbisddsm_training_data\": False},\n        3,\n    ),\n    (\"00009_PGGAN_POLYP_PATCHES_W_MASKS\", {\"save_option\": \"image_only\"}, 3),\n    (\"00010_FASTGAN_POLYP_PATCHES_W_MASKS\", {\"save_option\": \"image_only\"}, 3),\n    # (\"00011_SINGAN_POLYP_PATCHES_W_MASKS\", {\"checkpoint_ids\": [999]}, 3), # removed after successful testing due to limited CI pipeline capacity\n    # (\"00012_C-DCGAN_MMG_MASSES\", {\"condition\": 0}, 3), # removed after successful testing due to limited CI pipeline capacity\n    # (\"00013_CYCLEGAN_MMG_DENSITY_OPTIMAM_MLO\", {\"translate_all_images\": False}, 2), # removed after successful testing due to limited CI pipeline capacity\n    # (\"00014_CYCLEGAN_MMG_DENSITY_OPTIMAM_CC\", {\"translate_all_images\": False}, 2), # removed after successful testing due to limited CI pipeline capacity\n    # (\"00015_CYCLEGAN_MMG_DENSITY_CSAW_MLO\", {\"translate_all_images\": False}, 2), # removed after successful testing due to limited CI pipeline capacity\n    # (\"00016_CYCLEGAN_MMG_DENSITY_CSAW_CC\", {\"translate_all_images\": False}, 2), # removed after successful testing due to limited CI pipeline capacity\n    (\"00017_DCGAN_XRAY_LUNG_NODULES\", {}, 3),\n    (\"00018_WGANGP_XRAY_LUNG_NODULES\", {}, 3),\n    (\"00019_PGGAN_CHEST_XRAY\", {}, 3),\n    (\"00020_PGGAN_CHEST_XRAY\", {\"resize_pixel_dim\": 512, \"image_size\": 256}, 3),\n    (\n        \"00021_CYCLEGAN_BRAIN_MRI_T1_T2\",\n        {\n            \"input_path\": \"models/00021_CYCLEGAN_Brain_MRI_T1_T2/inputs/T2\",\n            \"gpu_id\": 0,\n            \"T1_to_T2\": False,\n        },\n        3,\n    ),\n    (\"00022_WGAN_CARDIAC_AGING\", {}, 3),\n    (\n        \"00023_PIX2PIXHD_BREAST_DCEMRI\",\n        {\n            \"input_path\": \"input\",\n            \"gpu_id\": 0,\n            \"image_size\": 448,\n        },\n        3,\n    ),\n]\n\n\n# class TestMediganExecutorMethods(unittest.TestCase):\nclass TestMediganExecutorMethods:\n    def setup_class(self):\n        ## unittest logger config\n        # This logger on root level initialized via logging.getLogger() will also log all log events\n        # from the medigan library. Pass a logger name (e.g. __name__) instead if you only want logs from tests.py\n        self.logger = logging.getLogger()  # (__name__)\n        self.logger.setLevel(LOGGING_LEVEL)\n        stream_handler = logging.StreamHandler(sys.stdout)\n        stream_handler.setLevel(LOGGING_LEVEL)\n        formatter = logging.Formatter(\n            \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n        )\n        stream_handler.setFormatter(formatter)\n        self.logger.addHandler(stream_handler)\n\n        self.test_output_path = \"test_output_path\"\n        self.num_samples = 2\n        self.test_imports_and_init_generators(self)\n        self._remove_dir_and_contents(self)  # in case something is left there.\n        self.model_ids = self.generators.config_manager.model_ids\n\n    def test_imports_and_init_generators(self):\n        from src.medigan.constants import (\n            CONFIG_FILE_KEY_EXECUTION,\n            CONFIG_FILE_KEY_GENERATE,\n            CONFIG_FILE_KEY_GENERATE_ARGS_INPUT_LATENT_VECTOR_SIZE,\n        )\n        from src.medigan.generators import Generators\n\n        self.generators = Generators()\n        self.CONFIG_FILE_KEY_EXECUTION = CONFIG_FILE_KEY_EXECUTION\n        self.CONFIG_FILE_KEY_GENERATE = CONFIG_FILE_KEY_GENERATE\n        self.CONFIG_FILE_KEY_GENERATE_ARGS_INPUT_LATENT_VECTOR_SIZE = (\n            CONFIG_FILE_KEY_GENERATE_ARGS_INPUT_LATENT_VECTOR_SIZE\n        )\n\n    @pytest.mark.parametrize(\"models_with_args\", [models_with_args])\n    def test_sample_generation_methods(self, models_with_args: list):\n        self.logger.debug(f\"models: {models_with_args}\")\n        for i, model_id in enumerate(self.model_ids):\n            # if (\n            #    model_id != \"00011_SINGAN_POLYP_PATCHES_W_MASKS\"\n            # ):\n            ## avoiding full memory on Windows ci test server\n            # continue\n            self.logger.debug(f\"Now testing model {model_id}\")\n            self._remove_dir_and_contents()  # Already done in each test independently, but to be sure, here again.\n            self.test_generate_method(model_id=model_id)\n\n            # Check if args available fo model_id. Note: The models list may not include the latest medigan models\n            for model in models_with_args:\n                if model_id == model[0]:\n                    self.test_generate_method_with_additional_args(\n                        model_id=model[0], args=model[1], expected_num_samples=model[2]\n                    )\n            self.test_get_generate_method(model_id=model_id)\n            self.test_get_dataloader_method(model_id=model_id)\n\n            # if i == 16:  # just for local testing\n            # self._remove_model_dir_and_zip(\n            #    model_ids=[model_id], are_all_models_deleted=False\n            # )\n\n    @pytest.mark.parametrize(\n        \"values_list, should_sample_be_generated\",\n        [\n            ([\"dcgan\", \"mMg\", \"ClF\", \"modality\", \"inbreast\"], True),\n            ([\"dcgan\", \"mMg\", \"ClF\", \"modality\", \"optimam\"], True),\n            ([\"dcgan\", \"mMg\", \"ClF\", \"modalities\"], False),\n        ],\n    )\n    def test_find_model_and_generate_method(\n        self, values_list, should_sample_be_generated\n    ):\n        self._remove_dir_and_contents()\n\n        self.generators.find_model_and_generate(\n            values=values_list,\n            target_values_operator=\"AND\",\n            are_keys_also_matched=True,\n            is_case_sensitive=False,\n            num_samples=self.num_samples,\n            output_path=self.test_output_path,\n        )\n\n        self._check_if_samples_were_generated(\n            should_sample_be_generated=should_sample_be_generated\n        )\n\n    @pytest.mark.parametrize(\n        \"values_list, metric\",\n        [\n            ([\"dcgan\", \"MMG\"], \"CLF.trained_on_real_and_fake.f1\"),\n            ([\"dcgan\", \"MMG\"], \"turing_test.AUC\"),\n        ],\n    )\n    def test_find_and_rank_models_then_generate_method(self, values_list, metric):\n        self._remove_dir_and_contents()\n        # TODO This test needs the respective metrics for any of these models to be available in config/global.json.\n        # These values would need to find at least two models.\n        self.generators.find_models_rank_and_generate(\n            values=values_list,\n            target_values_operator=\"AND\",\n            are_keys_also_matched=True,\n            is_case_sensitive=False,\n            metric=metric,\n            order=\"asc\",\n            num_samples=self.num_samples,\n            output_path=self.test_output_path,\n        )\n        self._check_if_samples_were_generated()\n\n    # @pytest.mark.parametrize(\"model_id\", [model[0] for model in models_with_args])\n    @pytest.mark.skip\n    def test_generate_method(self, model_id):\n        self._remove_dir_and_contents()\n        self.generators.generate(\n            model_id=model_id,\n            num_samples=self.num_samples,\n            output_path=self.test_output_path,\n            install_dependencies=True,\n        )\n        self._check_if_samples_were_generated(model_id=model_id)\n\n    # @pytest.mark.parametrize(\"model_id, args, expected_num_samples\", models_with_args)\n    @pytest.mark.skip\n    def test_generate_method_with_additional_args(\n        self, model_id, args, expected_num_samples\n    ):\n        self._remove_dir_and_contents()\n        self.generators.generate(\n            model_id=model_id,\n            num_samples=expected_num_samples,\n            output_path=self.test_output_path,\n            **args,\n        )\n        self._check_if_samples_were_generated(\n            model_id=model_id, num_samples=expected_num_samples\n        )\n\n    # @pytest.mark.parametrize(\"model_id\", [model[0] for model in models_with_args])\n    @pytest.mark.skip\n    def test_get_generate_method(self, model_id):\n        self._remove_dir_and_contents()\n        gen_function = self.generators.get_generate_function(\n            model_id=model_id,\n            num_samples=self.num_samples,\n            output_path=self.test_output_path,\n        )\n        gen_function()\n        self._check_if_samples_were_generated(model_id=model_id)\n        del gen_function\n\n    # @pytest.mark.parametrize(\"model_id\", [model[0] for model in models_with_args])\n    @pytest.mark.skip\n    def test_get_dataloader_method(self, model_id):\n        self._remove_dir_and_contents()\n        data_loader = self.generators.get_as_torch_dataloader(\n            model_id=model_id, num_samples=self.num_samples\n        )\n        self.logger.debug(f\"{model_id}: len(data_loader): {len(data_loader)}\")\n\n        if len(data_loader) != self.num_samples:\n            logging.warning(\n                f\"{model_id}: The number of samples in the dataloader (={len(data_loader)}) is not equal the number of samples requested (={self.num_samples}). \"\n                f\"Hint: Revise if the model's internal generate() function returned tuples as required in get_as_torch_dataloader().\"\n            )\n\n        #### Get the object at index 0 from the dataloader\n        data_dict = next(iter(data_loader))\n\n        # Test if the items at index [0] of the aforementioned object is of type torch tensor (e.g. torch.uint8) and not None, as expected by data structure design decision.\n        assert torch.is_tensor(data_dict.get(\"sample\"))\n\n        # Test if the items at index [1], [2] of the aforementioned object are None and, if not, whether they are of type torch tensor, as expected\n        assert data_dict.get(\"mask\") is None or torch.is_tensor(data_dict.get(\"mask\"))\n        assert data_dict.get(\"other_imaging_output\") is None or torch.is_tensor(\n            data_dict.get(\"other_imaging_output\")\n        )\n\n        # Test if the items at index [3] of the aforementioned object is None and, if not, whether it is of type list of strings, as expected.\n        assert data_dict.get(\"label\") is None or (\n            isinstance(data_dict.get(\"label\"), list)\n            and isinstance(data_dict.get(\"label\")[0], str)\n        )\n        del data_dict\n        del data_loader\n\n    # @pytest.mark.parametrize(\"model_id\", [model[0] for model in models_with_args])\n    @pytest.mark.skip\n    def test_visualize_method(self, model_id):\n        if (\n            self.CONFIG_FILE_KEY_GENERATE_ARGS_INPUT_LATENT_VECTOR_SIZE\n            in self.generators.config_manager.config_dict[model_id][\n                self.CONFIG_FILE_KEY_EXECUTION\n            ][self.CONFIG_FILE_KEY_GENERATE]\n        ):\n            self.generators.visualize(model_id, auto_close=True)\n\n        else:\n            with pytest.raises(Exception) as e:\n                self.generators.visualize(model_id, auto_close=True)\n\n                assert e.type == ValueError\n\n    @pytest.mark.skip\n    def _check_if_samples_were_generated(\n        self, model_id=None, num_samples=None, should_sample_be_generated: bool = True\n    ):\n        # check if the number of generated samples of model_id_1 is as expected.\n        file_list = glob.glob(self.test_output_path + \"/*\")\n        self.logger.debug(f\"{model_id}: {len(file_list)} == {self.num_samples} ?\")\n        if num_samples is None:\n            num_samples = self.num_samples\n\n        if should_sample_be_generated:\n            assert (\n                len(file_list) == num_samples\n                or len(file_list)\n                == num_samples\n                * 2\n                * 6  # 00007_INPAINT_BRAIN_MRI: 2 inpaints per sample, 6 outputs per sample\n                or len(file_list)\n                == num_samples * 2  # Temporary fix for different outputs per model.\n                or len(file_list) == num_samples + 1\n            ), f\"Model {model_id} generated {len(file_list)} samples instead of the expected {num_samples}, {num_samples*2*6}, or {num_samples + 1}.\"\n            # Some models are balanced per label by default: If num_samples is odd, then len(file_list)==num_samples +1\n        else:\n            assert len(file_list) == 0\n\n    # @pytest.mark.skip\n    def _remove_dir_and_contents(self):\n        \"\"\"After each test, empty the created folders and files to avoid corrupting a new test.\"\"\"\n\n        try:\n            shutil.rmtree(self.test_output_path)\n        except OSError as e:\n            # This may give an error if the folders are not created.\n            self.logger.debug(\n                f\"Exception while trying to delete folder. Likely it simply had not yet been created: {e}\"\n            )\n        except Exception as e2:\n            self.logger.error(f\"Error while trying to delete folder: {e2}\")\n\n    @pytest.mark.skip\n    def _remove_model_dir_and_zip(\n        self, model_ids=[], are_all_models_deleted: bool = False\n    ):\n        \"\"\"After a specific model folders, model_executor, and model zip file to avoid running out-of-disk space.\"\"\"\n\n        try:\n            for i, model_executor in enumerate(self.generators.model_executors):\n                if are_all_models_deleted or (\n                    model_ids is not None and model_executor.model_id in model_ids\n                ):\n                    try:\n                        # Delete the folder containing the model\n                        model_path = os.path.dirname(\n                            model_executor.deserialized_model_as_lib.__file__\n                        )\n                        shutil.rmtree(model_path)\n                        self.logger.info(\n                            f\"Deleted directory of model {model_executor.model_id}. ({model_path})\"\n                        )\n\n                    except OSError as e:\n                        # This may give an error if the FOLDER is not present\n                        self.logger.warning(\n                            f\"Exception while trying to delete the model folder of model {model_executor.model_id}: {e}\"\n                        )\n                    try:\n                        # If the downloaded zip package of the model was not deleted inside the model_path, we explicitely delete it now.\n                        if model_executor.package_path.is_file():\n                            os.remove(model_executor.package_path)\n                            self.logger.info(\n                                f\"Deleted zip file of model {model_executor.model_id}. ({model_executor.package_path})\"\n                            )\n                    except Exception as e:\n                        self.logger.warning(\n                            f\"Exception while trying to delete the ZIP file ({model_executor.package_path}) of model {model_executor.model_id}: {e}\"\n                        )\n            # Deleting the stateful model_executors instantiated by the generators module, after deleting folders and zips\n            if are_all_models_deleted:\n                self.generators.model_executors.clear()\n            else:\n                if model_ids is not None:\n                    for model_id in model_ids:\n                        model_executor = self.generators.find_model_executor_by_id(\n                            model_id\n                        )\n                        if model_executor is not None:\n                            self.generators.model_executors.remove(model_executor)\n                        del model_executor\n        except Exception as e2:\n            self.logger.error(\n                f\"Error while trying to delete model folders and zips: {e2}\"\n            )\n\n    # @pytest.fixture(scope=\"session\", autouse=True)\n    def teardown_class(self):\n        \"\"\"After all tests, empty the large model folders, model_executors, and zip files to avoid running out-of-disk space.\"\"\"\n\n        # yield is at test-time, signaling that things after yield are run after the execution of the last test has terminated\n        # https://docs.pytest.org/en/7.1.x/reference/reference.html?highlight=fixture#pytest.fixture\n        # yield None\n\n        # Remove all test outputs in test_output_path\n        self._remove_dir_and_contents(self)\n\n        # Remove all model folders, zip files and model executors\n        # self._remove_model_dir_and_zip(\n        #    self, model_ids=[\"00006_WGANGP_MMG_MASS_ROI\"], are_all_models_deleted=False\n        # )  # just for local testing\n        # self._remove_model_dir_and_zip(\n        #    self, model_ids=None, are_all_models_deleted=True\n        # )\n"
  },
  {
    "path": "tests/test_model_selector.py",
    "content": "# -*- coding: utf-8 -*-\n# ! /usr/bin/env python\n\"\"\" main test script to test the primary functions/classes/methods. \"\"\"\n# run with python -m tests.test_model_selector\n\nimport logging\nimport sys\n\nimport pytest\n\n# import unittest\n\n\n# Set the logging level depending on the level of detail you would like to have in the logs while running the tests.\nLOGGING_LEVEL = logging.INFO  # WARNING  # logging.INFO\n\nmodels = [\n    (\n        \"00001_DCGAN_MMG_CALC_ROI\",\n        {},\n        100,\n    ),\n    (\"00002_DCGAN_MMG_MASS_ROI\", {}, 3),\n    (\"00003_CYCLEGAN_MMG_DENSITY_FULL\", {\"translate_all_images\": False}, 2),\n    (\"00005_DCGAN_MMG_MASS_ROI\", {}, 3),\n    # Further models can be added here if/when needed.\n]\n\n\n# class TestMediganSelectorMethods(unittest.TestCase):\nclass TestMediganSelectorMethods:\n    def setup_method(self):\n        ## unittest logger config\n        # This logger on root level initialized via logging.getLogger() will also log all log events\n        # from the medigan library. Pass a logger name (e.g. __name__) instead if you only want logs from tests.py\n        self.logger = logging.getLogger()  # (__name__)\n        self.logger.setLevel(LOGGING_LEVEL)\n        stream_handler = logging.StreamHandler(sys.stdout)\n        stream_handler.setLevel(LOGGING_LEVEL)\n        formatter = logging.Formatter(\n            \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n        )\n        stream_handler.setFormatter(formatter)\n        self.logger.addHandler(stream_handler)\n        self.test_init_generators()\n\n    def test_init_generators(self):\n        from src.medigan.generators import Generators\n\n        self.generators = Generators()\n\n    @pytest.mark.parametrize(\n        \"values_list\",\n        [\n            ([\"dcgan\", \"mMg\", \"ClF\", \"modality\"]),\n            ([\"DCGAN\", \"Mammography\"]),\n        ],\n    )\n    def test_search_for_models_method(self, values_list):\n        found_models = self.generators.find_matching_models_by_values(\n            values=values_list,\n            target_values_operator=\"AND\",\n            are_keys_also_matched=True,\n            is_case_sensitive=False,\n        )\n        self.logger.debug(\n            f\"For value {values_list}, these models were found: {found_models}\"\n        )\n        assert len(found_models) > 0\n\n    @pytest.mark.parametrize(\n        \"models, values_list, metric\",\n        [\n            (\n                models,\n                [\"dcgan\", \"MMG\"],\n                \"CLF.trained_on_real_and_fake.f1\",\n            ),\n            (models, [\"dcgan\", \"MMG\"], \"turing_test.AUC\"),\n        ],\n    )\n    def test_find_and_rank_models_by_performance(self, models, values_list, metric):\n        # These values would need to find at least two models. See metrics and values in the config/global.json file.\n        found_ranked_models = self.generators.find_models_and_rank(\n            values=values_list,\n            target_values_operator=\"AND\",\n            are_keys_also_matched=True,\n            is_case_sensitive=False,\n            metric=metric,\n            order=\"desc\",\n        )\n        assert (\n            len(found_ranked_models) > 0  # some models were found as is expected\n            and found_ranked_models[0][\"model_id\"] is not None  # has a model id\n            and (\n                len(found_ranked_models) < 2\n                or found_ranked_models[0][metric] > found_ranked_models[1][metric]\n            )  # descending order (the higher a model's value, the lower its index in the list) is working\n        )\n\n    @pytest.mark.parametrize(\n        \"models, metric, order\",\n        [\n            (\n                models,\n                \"FID\",\n                \"asc\",\n            ),  # Note: normally a lower FID is better, therefore asc (model with lowest FID has lowest result list index).\n            (\n                models,\n                \"FID_RADIMAGENET_ratio\",\n                \"desc\",  # descending, as the higher the FID ratio the better.\n            ),\n            # Note: normally a lower FID is better, therefore asc (model with lowest FID has lowest result list index).\n            (models, \"CLF.trained_on_real_and_fake.f1\", \"desc\"),\n            (models, \"turing_test.AUC\", \"desc\"),\n        ],\n    )\n    def test_rank_models_by_performance(self, models, metric, order):\n        \"\"\"Ranking according to metrics in the config/global.json file.\"\"\"\n        ranked_models = self.generators.rank_models_by_performance(\n            model_ids=None,\n            metric=metric,\n            order=order,\n        )\n        assert (\n            len(ranked_models) > 0  # at least one model was found\n            and (\n                len(ranked_models) >= 21 or metric != \"FID\"\n            )  # we should find at least 21 models with FID in medigan\n            and ranked_models[0][\"model_id\"]\n            is not None  # found model has a model id (i.e. correctly formatted results)\n            and (\n                len(ranked_models) == 1\n                or (\n                    ranked_models[0][metric] > ranked_models[1][metric]\n                    or metric == \"FID\"\n                )\n            )  # descending order (the higher a model's value, the lower its index in the list) is working. In case of FID it is the other way around (ascending order is better).\n        )\n\n    @pytest.mark.parametrize(\n        \"models, metric, order\",\n        [\n            (models, \"CLF.trained_on_real_and_fake.f1\", \"desc\"),\n            (models, \"turing_test.AUC\", \"desc\"),\n        ],\n    )\n    def test_rank_models_by_performance_with_given_ids(self, models, metric, order):\n        \"\"\"Ranking a specified set of models according to metrics in the config/global.json file.\"\"\"\n        ranked_models = self.generators.rank_models_by_performance(\n            model_ids=[models[1][0], models[2][0]],\n            metric=metric,\n            order=order,\n        )\n        assert 0 < len(ranked_models) <= 2 and (\n            len(ranked_models) < 2\n            or (ranked_models[0][metric] > ranked_models[1][metric])\n        )  # checking if descending order (the higher a model's value, the lower its index in the list) is working.\n\n    @pytest.mark.parametrize(\n        \"key1, value1, expected\",\n        [\n            (\"modality\", \"Full-Field Mammography\", 2),\n            (\"license\", \"BSD\", 2),\n            (\"performance.CLF.trained_on_real_and_fake.f1\", \"0.96\", 0),\n            (\"performance.turing_test.AUC\", \"0.56\", 0),\n        ],\n    )\n    def test_get_models_by_key_value_pair(self, key1, value1, expected):\n        found_models = self.generators.get_models_by_key_value_pair(\n            key1=key1, value1=value1, is_case_sensitive=False\n        )\n        assert len(found_models) >= expected\n"
  }
]