Repository: CEA-COSMIC/pysap (language: python)

Issue:
Remove pre-installation of pybind11
- Pybind11 now installed by Sparse2D
- Removed pre-installation requirements and commands


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/cd-build.yml b/.github/workflows/cd-build.yml
index 26d1dd51..bef08c1e 100644
--- a/.github/workflows/cd-build.yml
+++ b/.github/workflows/cd-build.yml
@@ -16,24 +16,24 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v2
 
-      - name: Set up Conda with Python 3.8
-        uses: conda-incubator/setup-miniconda@v2
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v4
         with:
-          auto-update-conda: true
-          python-version: 3.8
-          auto-activate-base: false
+          python-version: ${{ matrix.python-version }}
+
+      - name: Check Python Version
+        run: python --version
 
       - name: Install Package Dependencies
-        shell: bash -l {0}
         run: |
-          python --version
           python -m pip install --upgrade pip
-          python -m pip install nose nose-exclude coverage pycodestyle
+          python -m pip install -r develop.txt
+
+      - name: Install PySAP
+        run: python -m pip install .
 
       - name: Run Tests
-        shell: bash -l {0}
-        run: |
-          python setup.py nosetests
+        run: python -m pytest
 
       - name: Upload coverage to Codecov
         uses: codecov/codecov-action@v1
@@ -41,3 +41,73 @@ jobs:
           token: ${{ secrets.CODECOV_TOKEN }}
           file: coverage.xml
           flags: unittests
+
+  api:
+    name: Deploy API Documentation
+    needs: coverage
+    runs-on: ubuntu-latest
+    if: success()
+
+    env:
+      gh_root: https://github.com/CEA-COSMIC/
+      tag_path: /archive/refs/tags/
+      sphinx_opts: -t docs/_templates -feTMo docs/source
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v4
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Check Python Version
+        run: python --version
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          python -m pip install pandoc
+          python -m pip install -r docs/requirements.txt
+          python -m pip install .
+
+      - name: Download plug-ins
+        if: ${{ inputs.build_plugins }}
+        run: |
+          while read _plugin; do
+            plugin_name="$(echo ${_plugin} | awk -F== '{print $1}')"
+            plugin_version="$(echo ${_plugin} | awk -F== '{print $2}')"
+            wget ${gh_root}${plugin_name}${tag_path}${plugin_version}.tar.gz -O ${plugin_name}.tar.gz
+          done < plugins.txt
+          for file in *.tar.gz; do tar xzf "$file"; done
+          rm *.tar.gz
+
+      - name: Extract plug-in examples
+        if: ${{ inputs.build_examples }}
+        run: |
+          while read _plugin; do
+            plugin_name="$(echo ${_plugin} | awk -F== '{print $1}')"
+            cp -r ${plugin_name}-*/examples examples/${plugin_name} || echo "Warning: no examples found for ${plugin_name}"
+          done < plugins.txt
+
+      - name: Build PySAP source files
+        run: sphinx-apidoc ${sphinx_opts} pysap
+
+      - name: Build plug-in source files
+        if: ${{ inputs.build_plugins }}
+        run: |
+          while read _plugin; do
+            plugin_name="$(echo ${_plugin} | awk -F== '{print $1}')"
+            package_name="$(echo ${plugin_name} | awk -F- '{print $2}')"
+            sphinx-apidoc ${sphinx_opts} ${plugin_name}-*/${package_name}
+          done < plugins.txt
+
+      - name: Build API documentation
+        run: sphinx-build -E docs/source docs/_build
+
+      - name: Deploy API documentation
+        uses: peaceiris/actions-gh-pages@v3.5.9
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          publish_dir: docs/_build
diff --git a/.github/workflows/check-docs.yml b/.github/workflows/check-docs.yml
new file mode 100644
index 00000000..8c61379d
--- /dev/null
+++ b/.github/workflows/check-docs.yml
@@ -0,0 +1,86 @@
+name: Check Docs Manually
+
+on:
+  workflow_dispatch:
+    inputs:
+      build_plugins:
+        description: 'Build API Docs with Plug-Ins'
+        required: true
+        default: true
+        type: boolean
+      build_examples:
+        description: 'Build API Docs with Examples'
+        required: true
+        default: true
+        type: boolean
+
+jobs:
+
+  api:
+    name: Check API Documentation
+    runs-on: ubuntu-latest
+
+    env:
+      gh_root: https://github.com/CEA-COSMIC/
+      tag_path: /archive/refs/tags/
+      sphinx_opts: -t docs/_templates -feTMo docs/source
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v4
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Check Python Version
+        run: python --version
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          python -m pip install pandoc
+          python -m pip install -r docs/requirements.txt
+          python -m pip install .
+
+      - name: Download plug-ins
+        if: ${{ inputs.build_plugins }}
+        run: |
+          while read _plugin; do
+            plugin_name="$(echo ${_plugin} | awk -F== '{print $1}')"
+            plugin_version="$(echo ${_plugin} | awk -F== '{print $2}')"
+            wget ${gh_root}${plugin_name}${tag_path}${plugin_version}.tar.gz -O ${plugin_name}.tar.gz
+          done < plugins.txt
+          for file in *.tar.gz; do tar xzf "$file"; done
+          rm *.tar.gz
+
+      - name: Extract plug-in examples
+        if: ${{ inputs.build_examples }}
+        run: |
+          while read _plugin; do
+            plugin_name="$(echo ${_plugin} | awk -F== '{print $1}')"
+            cp -r ${plugin_name}-*/examples examples/${plugin_name} || echo "Warning: no examples found for ${plugin_name}"
+          done < plugins.txt
+
+      - name: Build PySAP source files
+        run: sphinx-apidoc ${sphinx_opts} pysap
+
+      - name: Build plug-in source files
+        if: ${{ inputs.build_plugins }}
+        run: |
+          while read _plugin; do
+            plugin_name="$(echo ${_plugin} | awk -F== '{print $1}')"
+            package_name="$(echo ${plugin_name} | awk -F- '{print $2}')"
+            sphinx-apidoc ${sphinx_opts} ${plugin_name}-*/${package_name}
+          done < plugins.txt
+
+      - name: Build API documentation
+        run: sphinx-build -E docs/source docs/_build
+
+      - name: Archive API build
+        uses: actions/upload-artifact@v2
+        with:
+          name: api-docs
+          retention-days: 14
+          path: docs/_build
diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml
index 4c3d8f97..5a17913f 100644
--- a/.github/workflows/ci-build.yml
+++ b/.github/workflows/ci-build.yml
@@ -16,63 +16,55 @@ jobs:
       fail-fast: false
       matrix:
         os: [ubuntu-latest, macos-latest]
-        python-version: [3.8]
+        python-version: ["3.10"]
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
-      - name: Set up Conda with Python ${{ matrix.python-version }}
-        uses: conda-incubator/setup-miniconda@v2
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v4
         with:
-          auto-update-conda: true
           python-version: ${{ matrix.python-version }}
-          auto-activate-base: false
 
-      - name: Check Conda
-        shell: bash -l {0}
-        run: |
-          conda info
-          conda list
-          python --version
+      - name: Check Python Version
+        run: python --version
 
       - name: Install macOS Dependencies
-        shell: bash -l {0}
         if: runner.os == 'macOS'
         run: |
-          brew install libomp
+          brew tap sfarrens/sf
+          brew install bigmac libomp
 
       - name: Install Package Dependencies
-        shell: bash -l {0}
         run: |
-          python --version
           python -m pip install --upgrade pip
-          python -m pip install nose nose-exclude coverage pycodestyle
+          python -m pip install -r develop.txt
           python -m pip install twine
 
-      - name: Run Tests
-        shell: bash -l {0}
+      - name: Install PySAP
+        run: python -m pip install .
+
+      - name: Check PySAP Installation
         run: |
-          python setup.py nosetests
-          pycodestyle pysap --ignore="E121,E123,E126,E226,E24,E704,E402,E731,E722,E741,W503,W504,W605"
-          pycodestyle examples --ignore="E121,E123,E126,E226,E24,E704,E402,E731,E741,W503,W504,W605"
+          python -c "import pysparse"
+          python -c "import pysap; print(pysap.info())"
+
+      - name: Run Tests
+        run: python -m pytest
 
       - name: Save Test Results
         if: always()
         uses: actions/upload-artifact@v2
         with:
           name: unit-test-results-${{ matrix.os }}-${{ matrix.python-version }}
-          path: nosetests.xml
+          path: pytest.xml
 
       - name: Run Examples
         continue-on-error: true
-        shell: bash -l {0}
-        run: |
-          python -m pip install .
-          python pysap/test/test_examples.py
+        run: python pysap/test/test_examples.py
 
       - name: Check Distribution
-        shell: bash -l {0}
         run: |
           python setup.py sdist
           twine check dist/*
@@ -92,33 +84,39 @@ jobs:
       fail-fast: false
       matrix:
         os: [ubuntu-latest, macos-latest]
-        python-version: [3.6, 3.7, 3.9]
+        python-version: ["3.7", "3.8", "3.9"]
 
     steps:
       - name: Checkout
         uses: actions/checkout@v2
 
-      - name: Set up Conda with Python ${{ matrix.python-version }}
-        uses: conda-incubator/setup-miniconda@v2
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v4
         with:
-          auto-update-conda: true
           python-version: ${{ matrix.python-version }}
-          auto-activate-base: false
+
+      - name: Check Python Version
+        run: python --version
 
       - name: Install macOS Dependencies
-        shell: bash -l {0}
         if: runner.os == 'macOS'
         run: |
-          brew install libomp
+          brew tap sfarrens/sf
+          brew install bigmac libomp
 
       - name: Install Package Dependencies
-        shell: bash -l {0}
         run: |
           python --version
           python -m pip install --upgrade pip
-          python -m pip install nose nose-exclude coverage pycodestyle
+          python -m pip install -r develop.txt
 
-      - name: Run Tests
-        shell: bash -l {0}
+      - name: Install PySAP
+        run: python -m pip install .
+
+      - name: Check PySAP Installation
         run: |
-          python setup.py nosetests
+          python -c "import pysparse"
+          python -c "import pysap; print(pysap.info())"
+
+      - name: Run Tests
+        run: python -m pytest
diff --git a/.gitignore b/.gitignore
index 4fdd70f3..b90bdad3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,19 +1,150 @@
-# Compiled source #
-###################
-*.o
-*.so
-*.pyc
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
 
-# Logs and temporaries #
-########################
-*.log
-*~
-.coverage
+# C extensions
+*.so
 
-# Folders #
-###########
+# Distribution / packaging
+.Python
 build/
+develop-eggs/
 dist/
-python_pySAP.egg-info/
-__pycache__/
+downloads/
+eggs/
 .eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+pytest.xml
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+docs/source/auto_examples
+docs/source/fortuna.*
+docs/source/scripts.*
+docs/source/*.nblink
+docs/source/*astro*
+docs/source/*etomo*
+docs/source/*mri*
+docs/source/*pysap*
+pysap-astro*
+pysap-etomo*
+pysap-mri*
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+#   For a library or package, you might want to ignore these files since the code is
+#   intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
diff --git a/develop.txt b/develop.txt
new file mode 100644
index 00000000..3697a6b6
--- /dev/null
+++ b/develop.txt
@@ -0,0 +1,3 @@
+pytest
+pytest-cov
+pytest-pycodestyle
diff --git a/doc/macos_install.rst b/doc/macos_install.rst
deleted file mode 100644
index b0795629..00000000
--- a/doc/macos_install.rst
+++ /dev/null
@@ -1,142 +0,0 @@
-Mac OS Installation
-===================
-
-Some additional steps beyond the standard installation instructions may be
-required for certain macOS systems.
-
-The steps detailed in this document were last tested for **macOS 10.14.2**.
-
-
-Contents
-========
-
-1. `Requirements`_
-
-   1. `Xcode Command Line Tools`_
-   2. `Homebrew`_
-
-2. `Troubleshooting`_
-
-   1. `Python 3`_
-   2. `PyQtGraph`_
-   3. `Matplotlib`_
-   4. `CFITSIO`_
-
-Requirements
-============
-
-The following packages are required in order to build PySAP:
-
-1. ``cmake``
-
-2. ``libomp`` (or ``gcc``\*)
-
-  \*Any c/c++ compiler that supports OpenMP should work. (Note that the native macOS
-  ``clang`` does not provide OpenMP support on its own)
-
-
-Xcode Command Line Tools
-------------------------
-
-An essential first step for any developer working on macOS is to install the command line tools. This can be done as follows
-
-.. code-block:: bash
-
-  xcode-select --install
-
-Homebrew
---------
-
-The above listed requirements can be readily installed on macOS using |link-to-brew|.
-
-.. |link-to-brew| raw:: html
-
-  <a href="https://brew.sh/"
-  target="_blank">Homebrew</a>
-
-.. code-block:: bash
-
-  brew install cmake libomp
-
-Note that if you install ``gcc`` the commands ``gcc`` and ``g++`` default to ``clang``.
-Therefore, before installing PySAP, you should export the environment variables ``CC`` and ``CXX``.
-
-*e.g.*
-
-.. code-block:: bash
-
-  export CC="/usr/local/bin/gcc-8"
-  export CXX="/usr/local/bin/g++-8"
-
-If you encounter problems re-compiling PySAP following an OS update it may be necessary to uninstall Homebrew and repeat these steps.
-
-Troubleshooting
-===============
-
-The following subsections propose solutions to some known issues.
-
-PyQtGraph
----------
-
-Some issues may arise at runtime with regards to the installation of ``pyqtgraph``. The
-easiest solution to this problem is to install the package using |link-to-conda|.
-
-.. |link-to-conda| raw:: html
-
-  <a href="https://conda.io/docs/"
-  target="_blank">Anaconda</a>
-
-.. code-block:: bash
-
-  conda install pyqtgraph
-
-Alternatively reinstall PyQT5.
-
-.. code-block:: bash
-
-  pip install -I pyqt5
-
-See the |link-to-pyqt| for further help.
-
-.. |link-to-pyqt| raw:: html
-
-  <a href="http://www.pyqtgraph.org/"
-  target="_blank">PyQtGraph homepage</a>
-
-Matplotlib
-----------
-
-If you see the following error or something similar
-
-.. code-block:: bash
-
-  **RuntimeError**: Python is not installed as a framework...
-
-simply create a ``matplotlibrc`` file and specify a backend.
-
-*e.g.*
-
-.. code-block:: bash
-
-  echo "backend: Agg" >> ~/.matplotlib/matplotlibrc
-
-CFITSIO
--------
-
-If you encounter this error
-
-.. code-block:: bash
-
-  configure: error: cannot run C compiled programs.
-  If you meant to cross compile, use `--host'.
-  See `config.log' for more details
-  make[2]: *** [cfitsio/src/cfitsio-stamp/cfitsio-configure] Error 1
-  make[1]: *** [CMakeFiles/cfitsio.dir/all] Error 2
-  make[1]: *** Waiting for unfinished jobs....
-
-It may be necessary to install the macOS SDK headers. This can be done as follows
-
-.. code-block:: bash
-
-  cd /Library/Developer/CommandLineTools/Packages/
-  open macOS_SDK_headers_for_macOS_10.14.pkg
diff --git a/doc/source/_static/carousel/best_ssim_magnitude.png b/doc/source/_static/carousel/best_ssim_magnitude.png
deleted file mode 100644
index 34e6a24f..00000000
Binary files a/doc/source/_static/carousel/best_ssim_magnitude.png and /dev/null differ
diff --git a/doc/source/_static/carousel/dirty_astro.png b/doc/source/_static/carousel/dirty_astro.png
deleted file mode 100644
index e983be73..00000000
Binary files a/doc/source/_static/carousel/dirty_astro.png and /dev/null differ
diff --git a/doc/source/_static/carousel/galaxy.png b/doc/source/_static/carousel/galaxy.png
deleted file mode 100644
index ce912c2c..00000000
Binary files a/doc/source/_static/carousel/galaxy.png and /dev/null differ
diff --git a/doc/source/_static/carousel/orange_picture.png b/doc/source/_static/carousel/orange_picture.png
deleted file mode 100644
index ad7661a9..00000000
Binary files a/doc/source/_static/carousel/orange_picture.png and /dev/null differ
diff --git a/doc/source/_static/carousel/sparkling_nc_26.png b/doc/source/_static/carousel/sparkling_nc_26.png
deleted file mode 100644
index 2c576651..00000000
Binary files a/doc/source/_static/carousel/sparkling_nc_26.png and /dev/null differ
diff --git a/doc/source/_static/carousel/sparse_reconstruction_via_condatvu_astro.png b/doc/source/_static/carousel/sparse_reconstruction_via_condatvu_astro.png
deleted file mode 100644
index 512f850c..00000000
Binary files a/doc/source/_static/carousel/sparse_reconstruction_via_condatvu_astro.png and /dev/null differ
diff --git a/doc/source/_static/carousel/spiral_nc_26.png b/doc/source/_static/carousel/spiral_nc_26.png
deleted file mode 100644
index a0d0f5e3..00000000
Binary files a/doc/source/_static/carousel/spiral_nc_26.png and /dev/null differ
diff --git a/doc/source/_static/pysap.png b/doc/source/_static/pysap.png
deleted file mode 100644
index 61f4e840..00000000
Binary files a/doc/source/_static/pysap.png and /dev/null differ
diff --git a/doc/source/conf.py b/doc/source/conf.py
deleted file mode 100644
index 555f40f7..00000000
--- a/doc/source/conf.py
+++ /dev/null
@@ -1,282 +0,0 @@
-#! /usr/bin/env python
-##########################################################################
-# pysphinxdoc - Copyright (C) AGrigis, 2016
-# Distributed under the terms of the CeCILL-B license, as published by
-# the CEA-CNRS-INRIA. Refer to the LICENSE file or to
-# http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
-# for details.
-##########################################################################
-
-# System import
-import sys
-import os
-from distutils.version import LooseVersion
-import subprocess
-import sphinx
-import pysphinxdoc
-
-installdir = os.path.abspath("../..")
-env = os.environ
-if "PYTHONPATH" in env:
-    env["PYTHONPATH"] = env["PYTHONPATH"] + ":" + installdir
-else:
-    env["PYTHONPATH"] = installdir
-cmd = ["sphinxdoc", "-v 2", "-p",  installdir, "-n", "pysap", "-o", "..",
-       "-i", "python-pySAP"]
-subprocess.check_call(cmd, env=env)
-sys.path.insert(0, installdir)
-
-if LooseVersion(sphinx.__version__) < LooseVersion("1"):
-    raise RuntimeError("Need sphinx >= 1 for autodoc to work correctly.")
-if LooseVersion(sphinx.__version__) < LooseVersion("1.8"):
-    sphinx_math = "sphinx.ext.pngmath"
-else:
-    sphinx_math = "sphinx.ext.imgmath"
-
-# -- General configuration --------------------------------------------------
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here.
-sphinx_dirname = os.path.dirname(pysphinxdoc.__file__)
-sys.path.insert(0, os.path.join(sphinx_dirname, "sphinxext"))
-
-# Add any Sphinx extension module names here, as strings.
-# They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = [
-    "sphinx.ext.autodoc",
-    "sphinx.ext.doctest",
-    "sphinx.ext.intersphinx",
-    "sphinx.ext.todo",
-    "sphinx.ext.coverage",
-    sphinx_math,
-    "sphinx.ext.ifconfig",
-    "sphinx.ext.autosummary",
-    "sphinx.ext.viewcode",
-    "numpy_ext.numpydoc",
-    "custom_ext.hidden_code_block",
-    "custom_ext.hidden_technical_block",
-    "custom_ext.link_to_block",
-    "sphinx_gallery.gen_gallery"]
-
-# Configure gallery
-sphinx_gallery_conf = {
-    "doc_module": "pysap",
-    "backreferences_dir": os.path.join("generated", "gallery"),
-    "examples_dirs": os.path.join(os.pardir, "examples"),
-    "gallery_dirs": "auto_gallery"}
-
-# Remove some numpy-linked warnings
-numpydoc_show_class_members = False
-
-# generate autosummary even if no references
-autosummary_generate = True
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = [
-    os.path.join(sphinx_dirname, "templates"),
-    os.path.join("generated", "_templates")]
-
-# The suffix of source filenames.
-source_suffix = ".rst"
-
-# The encoding of source files.
-# source_encoding = 'utf-8'
-
-# The master toctree document.
-master_doc = "index"
-
-# General information about the project.
-project = u"pysap"
-copyright = u"""2019, 
-Antoine Grigis <antoine.grigis@cea.fr>
-Samuel Farrens <samuel.farrens@cea.fr>
-Jean-Luc Starck <jl.stark@cea.fr>
-Philippe Ciuciu <philippe.ciuciu@cea.fr>
- <antoine.grigis@cea.fr>"""
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = "0.0.3"
-# The full version, including alpha/beta/rc tags.
-release = version
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-# language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-# today = ''
-# Else, today_fmt is used as the format for a strftime call.
-# today_fmt = ''
-
-# List of documents that shouldn't be included in the build.
-# unused_docs = []
-
-# List of directories, relative to source directory, that shouldn't be searched
-# for source files.
-exclude_patterns = [
-    "examples",
-    templates_path[0],
-    templates_path[1],
-    os.path.join("scikit-learn", "static", "ML_MAPS_README.rst")]
-
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-# default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-# add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-# show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = "sphinx"
-
-# A list of ignored prefixes for module index sorting.
-# modindex_common_prefix = []
-
-
-# -- Options for HTML output ------------------------------------------------
-
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = "sphinx"
-
-# The theme to use for HTML and HTML Help pages.  Major themes that come with
-# Sphinx are currently 'default' and 'sphinxdoc'.
-html_theme = "azmind"
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-html_theme_options = {
-    "oldversion": False,
-    "collapsiblesidebar": True,
-    "google_analytics": True,
-    "surveybanner": False,
-    "sprintbanner": True}
-
-# Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = [
-    os.path.join(sphinx_dirname, "themes")]
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-html_title = "python-pySAP"
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-html_short_title = "python-pySAP"
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-# html_logo = ""
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-# html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = [
-    "_static"
-]
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-# html_last_updated_fmt = ''
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-# html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-# html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-# html_additional_pages = {}
-
-# If false, no module index is generated.
-html_use_modindex = False
-
-# If false, no index is generated.
-html_use_index = False
-
-# If true, the index is split into individual pages for each letter.
-# html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-html_show_sourcelink = False
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-# html_use_opensearch = ''
-
-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-# html_file_suffix = ''
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = "python-pySAP"
-
-
-# -- Options for LaTeX output -----------------------------------------------
-
-# The paper size ('letter' or 'a4').
-# latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-# latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass
-# [howto/manual]).
-latex_documents = [
-    ("index", "pysap.tex", "pysap Documentation", """
-Antoine Grigis <antoine.grigis@cea.fr>
-Samuel Farrens <samuel.farrens@cea.fr>
-Jean-Luc Starck <jl.stark@cea.fr>
-Philippe Ciuciu <philippe.ciuciu@cea.fr>
-""",
-     "manual"),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-# latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-# latex_use_parts = False
-
-# Additional stuff for the LaTeX preamble.
-# latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-# latex_appendices = []
-
-# If false, no module index is generated.
-# latex_use_modindex = True
-
-
-# Example configuration for intersphinx: refer to the Python standard library.
-# intersphinx_mapping = {'http://docs.python.org/': None}
-
-
-# -- Options for Texinfo output ---------------------------------------------
-
-autodoc_default_flags = ["members", "undoc-members"]
-
diff --git a/docs/_templates/module.rst_t b/docs/_templates/module.rst_t
new file mode 100644
index 00000000..73ad73fc
--- /dev/null
+++ b/docs/_templates/module.rst_t
@@ -0,0 +1,8 @@
+{%- if show_headings %}
+{{- basename | e | heading(2) }}
+
+{% endif -%}
+.. automodule:: {{ qualname }}
+{%- for option in automodule_options %}
+   :{{ option }}:
+{%- endfor %}
diff --git a/docs/_templates/package.rst_t b/docs/_templates/package.rst_t
new file mode 100644
index 00000000..344f9d16
--- /dev/null
+++ b/docs/_templates/package.rst_t
@@ -0,0 +1,54 @@
+{%- macro automodule(modname, options) -%}
+.. automodule:: {{ modname }}
+{%- for option in options %}
+   :{{ option }}:
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro toctree(docnames) -%}
+.. toctree::
+   :maxdepth: {{ maxdepth }}
+{% for docname in docnames %}
+   {{ docname }}
+{%- endfor %}
+{%- endmacro %}
+
+{%- if is_namespace %}
+{{- [pkgname, "namespace"] | join(" ") | e | heading }}
+{% else %}
+{{- pkgname | e | heading }}
+
+{% endif %}
+
+{%- if modulefirst and not is_namespace %}
+{{ automodule(pkgname, automodule_options) }}
+{% endif %}
+
+{%- if subpackages %}
+Subpackages
+-----------
+
+{{ toctree(subpackages) }}
+{% endif %}
+
+{%- if submodules %}
+Submodules
+----------
+{% if separatemodules %}
+{{ toctree(submodules) }}
+{% else %}
+{%- for submodule in submodules %}
+{% if show_headings %}
+{{- submodule | e | heading(2) }}
+{% endif %}
+{{ automodule(submodule, automodule_options) }}
+{% endfor %}
+{%- endif %}
+{%- endif %}
+
+{%- if not modulefirst and not is_namespace %}
+Module contents
+---------------
+
+{{ automodule(pkgname, automodule_options) }}
+{% endif %}
diff --git a/docs/_templates/toc.rst_t b/docs/_templates/toc.rst_t
new file mode 100644
index 00000000..f0877eeb
--- /dev/null
+++ b/docs/_templates/toc.rst_t
@@ -0,0 +1,8 @@
+{{ header | heading }}
+
+.. toctree::
+   :maxdepth: {{ maxdepth }}
+{% for docname in docnames %}
+   {{ docname }}
+{%- endfor %}
+
diff --git a/docs/images/banner-astro.jpg b/docs/images/banner-astro.jpg
new file mode 100644
index 00000000..13547244
Binary files /dev/null and b/docs/images/banner-astro.jpg differ
diff --git a/docs/images/banner-etomo.jpg b/docs/images/banner-etomo.jpg
new file mode 100644
index 00000000..9587f1b2
Binary files /dev/null and b/docs/images/banner-etomo.jpg differ
diff --git a/docs/images/banner-mri.jpg b/docs/images/banner-mri.jpg
new file mode 100644
index 00000000..769a6004
Binary files /dev/null and b/docs/images/banner-mri.jpg differ
diff --git a/docs/images/cosmostat_logo.jpg b/docs/images/cosmostat_logo.jpg
new file mode 100644
index 00000000..8bf52ad5
Binary files /dev/null and b/docs/images/cosmostat_logo.jpg differ
diff --git a/docs/images/logo.png b/docs/images/logo.png
new file mode 100644
index 00000000..e89437f1
Binary files /dev/null and b/docs/images/logo.png differ
diff --git a/docs/images/logo.svg b/docs/images/logo.svg
new file mode 100644
index 00000000..450d9243
--- /dev/null
+++ b/docs/images/logo.svg
@@ -0,0 +1,983 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="165.76759mm"
+   height="66.364822mm"
+   viewBox="0 0 165.76759 66.364822"
+   version="1.1"
+   id="svg2209"
+   inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+   sodipodi:docname="logo.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview2211"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:document-units="mm"
+     showgrid="false"
+     inkscape:zoom="0.74208053"
+     inkscape:cx="297.13756"
+     inkscape:cy="406.29014"
+     inkscape:window-width="1920"
+     inkscape:window-height="1048"
+     inkscape:window-x="0"
+     inkscape:window-y="32"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="layer1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <defs
+     id="defs2206">
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2504"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2508"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2512"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2516"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7239"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7243"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7247"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7251"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2588"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2598"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2608"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2618"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2504-3"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2508-6"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2512-7"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect2516-5"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7239-3"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7243-5"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7247-6"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7251-2"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect3487"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect3497"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect3507"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect3517"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7239-9"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7243-1"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7247-2"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7251-7"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4203"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4213"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4223"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4233"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4243"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4253"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4263"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4273"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3334"
+       id="linearGradient2238-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0137942,0,0,1.0137942,-32.778524,-190.35889)"
+       x1="108.66066"
+       y1="201.46254"
+       x2="53.166351"
+       y2="158.46756" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3334">
+      <stop
+         style="stop-color:#ae0000;stop-opacity:0.15000001"
+         offset="0"
+         id="stop3332" />
+      <stop
+         style="stop-color:#ae0000;stop-opacity:0.5"
+         offset="1"
+         id="stop3330" />
+    </linearGradient>
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7239-0"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3824"
+       id="linearGradient2240-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0137942,0,0,1.0137942,-32.778524,-190.35889)"
+       x1="18.407795"
+       y1="186.14467"
+       x2="44.293537"
+       y2="163.40614" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3824">
+      <stop
+         style="stop-color:#ae0000;stop-opacity:0.69999999"
+         offset="0"
+         id="stop3820" />
+      <stop
+         style="stop-color:#ae0000;stop-opacity:0.5"
+         offset="1"
+         id="stop3822" />
+    </linearGradient>
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7243-9"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient9575"
+       id="linearGradient2242-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0137942,0,0,1.0137942,-32.778524,-190.35889)"
+       x1="13.860361"
+       y1="193.45227"
+       x2="27.366322"
+       y2="217.73978" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient9575">
+      <stop
+         style="stop-color:#ae0000;stop-opacity:0.69999999"
+         offset="0"
+         id="stop9573" />
+      <stop
+         style="stop-color:#ae0000;stop-opacity:0.89999998"
+         offset="1"
+         id="stop9571" />
+    </linearGradient>
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7247-3"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient9565"
+       id="linearGradient7253"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0137942,0,0,1.0137942,-32.778524,-190.35889)"
+       x1="-2.4234502"
+       y1="33.475479"
+       x2="29.203478"
+       y2="44.641441" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient9565">
+      <stop
+         style="stop-color:#ae0000;stop-opacity:0.89999998"
+         offset="0"
+         id="stop9563" />
+      <stop
+         style="stop-color:#ae0000;stop-opacity:1;"
+         offset="1"
+         id="stop9561" />
+    </linearGradient>
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect7251-6"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4726"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4736"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4746"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4756"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4766"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="18.3"
+       normal_offset="0"
+       tang_offset="-1.9"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4776"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="7.2"
+       normal_offset="0"
+       tang_offset="7"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4786"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.9"
+       normal_offset="0"
+       tang_offset="4.2"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect4796"
+       is_visible="true"
+       lpeversion="1"
+       pattern="m 167.88612,141.5743 a 5.9268909,5.5210609 0 0 1 -5.92689,5.52106 5.9268909,5.5210609 0 0 1 -5.92689,-5.52106 5.9268909,5.5210609 0 0 1 5.92689,-5.52107 5.9268909,5.5210609 0 0 1 5.92689,5.52107 z"
+       copytype="repeated"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0.4"
+       normal_offset="0"
+       tang_offset="-2.8"
+       prop_units="false"
+       vertical_pattern="false"
+       hide_knot="false"
+       fuse_tolerance="0" />
+  </defs>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-26.549305,-41.029354)">
+    <g
+       id="g1839"
+       inkscape:export-filename="/home/pac/Documents/0-These/pysap-logo1.png"
+       inkscape:export-xdpi="96"
+       inkscape:export-ydpi="96"
+       transform="matrix(0.98639347,0,0,0.98639347,-7.4397723,59.751482)"
+       style="stroke-width:1.0138">
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:57.9751px;line-height:1.25;font-family:sans-serif;fill:#446785;fill-opacity:1;stroke:#446785;stroke-width:0.502055;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         x="32.228844"
+         y="23.00263"
+         id="text103858-5-0"
+         transform="scale(1.0080466,0.99201763)"><tspan
+           sodipodi:role="line"
+           id="tspan103856-0-3"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Nimbus Mono PS';-inkscape-font-specification:'Nimbus Mono PS';fill:#446785;fill-opacity:1;stroke:#446785;stroke-width:0.502055;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           x="32.228844"
+           y="23.00263">Py AP</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.4258px;line-height:1.25;font-family:'Roboto Condensed';-inkscape-font-specification:'Roboto Condensed, ';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.66418"
+         x="33.582031"
+         y="45.861866"
+         id="text22761-9-0"><tspan
+           sodipodi:role="line"
+           id="tspan22759-6-4"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.4258px;font-family:'Roboto Condensed';-inkscape-font-specification:'Roboto Condensed, ';stroke-width:0.66418"
+           x="33.582031"
+           y="45.861866">Python Sparse data Analysis Package</tspan></text>
+      <g
+         id="g1903-8-4"
+         style="fill:#ae0000;fill-opacity:1;stroke-width:2.30379"
+         transform="matrix(1.0896339,0,0,1.0896339,43.585055,-213.65019)">
+        <g
+           id="g118758-0-4"
+           style="fill:#ae0000;fill-opacity:1;stroke-width:7.67784"
+           transform="matrix(0.30005708,0,0,0.30005708,61.443005,187.64544)">
+          <path
+             id="path113724-9-8-8-8-4"
+             style="fill:url(#linearGradient2238-3);fill-opacity:1;stroke:none;stroke-width:2.03143px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             inkscape:path-effect="#path-effect7239-0"
+             inkscape:original-d="M 72.366297,11.631887 C 70.848847,-14.48957 42.444565,-29.539959 18.638334,-23.026931"
+             d="m 70.41925,1.9046371 c 1.383013,-0.4833402 2.921698,-0.3511972 4.275179,0.5432356 1.349963,0.8921081 2.338084,2.4407556 2.650325,4.2452251 0.312233,1.8044264 -0.09762,3.5986632 -1.070841,4.8943402 -0.976026,1.299407 -2.382472,1.937971 -3.848805,1.943517 -3.019141,0.01142 -5.522833,-2.431466 -5.95934,-4.9559909 -0.437539,-2.5305043 1.103226,-5.6742143 3.953482,-6.670327 z M 50.545971,-19.753983 c 1.010384,-2.041068 3.318566,-3.210409 5.979474,-2.584459 0,0 0,0 1e-6,0 0.785188,0.183626 1.547381,0.517405 2.236219,0.981942 3.227968,2.176866 3.60469,5.990332 1.570749,8.233356 -2.031264,2.240079 -5.54249,2.42263 -7.757331,0.922133 -0.52874,-0.358208 -0.993106,-0.806763 -1.379312,-1.331454 0,0 0,-10e-7 0,-10e-7 -1.261276,-1.701061 -1.65318,-4.194607 -0.6498,-6.221517 z m -29.35774,-3.885255 c -0.615067,-2.965636 1.635513,-6.036747 5.485308,-6.301623 3.839156,-0.264142 6.485062,2.465947 6.293112,5.490135 -0.183112,2.885012 -2.836803,5.331134 -5.544337,5.51278 -2.685755,0.180185 -5.619874,-1.739775 -6.234083,-4.701292 z"
+             sodipodi:nodetypes="ccc" />
+          <path
+             style="fill:url(#linearGradient2240-6);fill-opacity:1;stroke:none;stroke-width:2.03143px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 1.194368,-16.171419 c -1.64884381,-2.54407 -0.6594519,-6.214488 2.8018873,-7.864395 3.4416552,-1.640524 6.9053727,-0.102083 7.8662717,2.776772 0.916429,2.745605 -0.657192,6.024516 -3.1322834,7.19382 -2.4591557,1.161774 -5.8917023,0.430674 -7.5358756,-2.106197 z m -12.577519,13.9798847 c -1.323073,-0.6175495 -2.326129,-1.7837804 -2.642796,-3.3938391 -0.31541,-1.6036652 0.109036,-3.4286427 1.198982,-4.9342396 2.382828,-3.291515 6.2731525,-3.381889 8.3637997,-1.203287 2.1016612,2.1900734 2.0391173,5.6960329 0.5471792,7.7126524 -1.465884,1.9814013 -4.7646822,3.08011401 -7.4671649,1.8187133 z"
+             id="path113724-9-8-5-7-7"
+             sodipodi:nodetypes="cc"
+             inkscape:path-effect="#path-effect7243-9"
+             inkscape:original-d="M 18.638334,-23.026931 C 4.9941328,-20.404195 -9.0593284,-12.048421 -12.973608,2.1071318" />
+          <path
+             style="fill:url(#linearGradient2242-5);fill-opacity:1;stroke:none;stroke-width:2.03143px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m -12.923128,17.98897 c -1.419146,0.366753 -2.944604,0.106511 -4.218792,-0.917322 -1.270854,-1.021154 -2.116239,-2.679738 -2.246424,-4.536243 -0.130175,-1.856378 0.475204,-3.6236811 1.59203,-4.8155985 1.120419,-1.1957531 2.593304,-1.6614025 4.04797,-1.4910948 2.983737,0.3493215 5.1999721,3.0473992 5.379095,5.5239263 0.180039,2.489206 -1.634109,5.481775 -4.553879,6.236332 z m 5.5480177,11.394709 c -1.1577024,0.896682 -2.6602897,1.258441 -4.2407727,0.80654 -1.575358,-0.450436 -3.008209,-1.66271 -3.849577,-3.332276 -1.157876,-2.29763 -0.912958,-4.839372 0.412956,-6.457665 0.62832,-0.767081 1.453287,-1.279712 2.366643,-1.542851 0.911488,-0.262602 1.883123,-0.2698 2.8067377,-0.08006 1.930117,0.393833 3.5192467,1.577111 4.2895742,3.111733 1.1057455,2.202834 0.5854256,5.658168 -1.7855612,7.494578 z"
+             id="path113724-9-8-5-0-1-6"
+             sodipodi:nodetypes="cc"
+             inkscape:path-effect="#path-effect7247-3"
+             inkscape:original-d="m -12.973608,2.1071318 c -2.992644,11.4257422 0.942622,24.5258622 10.21242,31.9987172" />
+          <path
+             style="fill:url(#linearGradient7253);fill-opacity:1;stroke:none;stroke-width:2.03143px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 5.2404887,38.22434 c -1.0114337,2.836478 -4.54861059,4.35231 -8.0918835,2.346246 -1.6303602,-0.923047 -2.80934,-2.422252 -3.1880493,-4.032929 -0.3810553,-1.620654 0.090929,-3.10275 1.0938073,-4.205875 0.9901924,-1.089175 2.4133747,-1.732673 3.7630565,-1.944237 1.36328069,-0.213696 2.6636837,-8.51e-4 3.7562263,0.620878 2.1437622,1.219945 3.6912819,4.342981 2.6668427,7.215917 z m 11.9481123,2.702188 c -0.349894,2.508689 -2.51866,4.477611 -5.455959,4.5481 0,0 -1e-6,0 -1e-6,0 -0.511129,0.01558 -1.028159,-0.02923 -1.538077,-0.134596 C 8.4944989,44.988756 7.0121194,43.994787 6.1466627,42.640397 5.278563,41.28187 5.1385658,39.745698 5.6182868,38.356938 6.601816,35.509671 9.7611134,33.909846 12.500811,34.514831 c 0.42056,0.09287 0.831405,0.233858 1.22616,0.420794 0,0 10e-7,10e-7 10e-7,10e-7 2.277706,1.046772 3.814063,3.46403 3.461629,5.990902 z m 12.155113,1.561579 c -0.459334,2.993129 -3.370446,5.084569 -6.533137,4.706005 -3.269161,-0.391309 -5.640538,-3.175309 -5.225847,-6.212928 0.409516,-2.999751 3.305382,-5.142346 6.539422,-4.75479 3.339879,0.400241 5.684705,3.230768 5.219562,6.261713 z"
+             id="path113724-9-8-5-9-6-7-3"
+             sodipodi:nodetypes="cc"
+             inkscape:path-effect="#path-effect7251-6"
+             inkscape:original-d="m -2.761188,34.105849 c 9.3100197,6.753825 21.276755,6.713616 32.167631,8.391899" />
+        </g>
+        <use
+           x="0"
+           y="0"
+           xlink:href="#g118758-0-4"
+           id="use3032-1"
+           transform="rotate(180,70.247647,200.39427)"
+           width="100%"
+           height="100%"
+           style="stroke-width:2.30379" />
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/docs/images/logo_no_background.png b/docs/images/logo_no_background.png
new file mode 100644
index 00000000..9fa83c9b
Binary files /dev/null and b/docs/images/logo_no_background.png differ
diff --git a/docs/images/neurospin_logo.png b/docs/images/neurospin_logo.png
new file mode 100644
index 00000000..4efb9cab
Binary files /dev/null and b/docs/images/neurospin_logo.png differ
diff --git a/doc/images/schema.jpg b/docs/images/schema.jpg
similarity index 100%
rename from doc/images/schema.jpg
rename to docs/images/schema.jpg
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 00000000..421e5ee2
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,9 @@
+jupyter==1.0.0
+nbsphinx==0.8.7
+nbsphinx-link==1.3.0
+numpydoc==1.1.0
+sphinx==4.3.1
+sphinx-copybutton==0.5.0
+sphinx-gallery==0.10.1
+sphinxcontrib-bibtex==2.4.1
+sphinx-book-theme==0.3.2
diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css
new file mode 100644
index 00000000..8318965a
--- /dev/null
+++ b/docs/source/_static/custom.css
@@ -0,0 +1,25 @@
+/* Change colour of sphinx-gallery download buttons */
+div.sphx-glr-download a {
+  background-color: #eef5fa !important;
+  background-image: none !important;
+  border: 1px solid #c9c9c9 !important;
+}
+
+/* Change size of sphinx-gallery thumbnails */
+.sphx-glr-thumbcontainer {
+    min-width: 250px !important;
+    margin: 20px !important;
+    padding-bottom: 50px !important;
+}
+.sphx-glr-thumbcontainer img {
+    max-height: 250px !important;
+    width: 250px !important;
+}
+.sphx-glr-thumbcontainer a.internal {
+    padding: 180px 10px 0 !important;
+}
+
+/* Hide sphinx-gallery signature */
+p.sphx-glr-signature a.reference.external {
+  font-size: 0% !important;
+}
diff --git a/docs/source/about.rst b/docs/source/about.rst
new file mode 100644
index 00000000..29f95dc5
--- /dev/null
+++ b/docs/source/about.rst
@@ -0,0 +1,109 @@
+About
+=====
+
+.. figure:: ../images/cosmostat_logo.jpg
+  :figclass: margin
+  :width: 250px
+  :alt: CosmoStat Logo
+  :target: http://www.cosmostat.org/
+
+.. figure:: ../images/neurospin_logo.png
+  :figclass: margin
+  :width: 250px
+  :alt: NeuroSpin Logo
+  :target: https://joliot.cea.fr/drf/joliot/en/Pages/research_entities/NeuroSpin.aspx
+
+The Python Sparse data Analysis Package (PySAP) was developed
+as part of |link-to-cosmic|, a multi-disciplinary collaboration between
+|link-to-neurospin|, experts in biomedical imaging, and |link-to-cosmostat|,
+experts in astrophysical image processing. The package was designed to provide
+state-of-the-art signal processing tools for a variety of imaging domains such
+as astronomy, electron tomography and magnetic resonance imaging (MRI). The
+first release of PySAP was presented in :cite:t:`farrens:2020`.
+
+Structure
+---------
+
+.. figure:: ../images/schema.jpg
+  :figclass: margin-caption
+  :width: 60%
+  :alt: PySAP Schema
+
+  Illustration of the structure of the PySAP package :cite:`farrens:2020`. The
+  SPARSE2D and ModOpt core libraries are represented in orange and red,
+  respectively. The various plug-in applications appear in blue.
+
+PySAP is comprised of several core modules, namely:
+
+
+.. figure:: https://cea-cosmic.github.io/ModOpt/_images/modopt_logo.png
+  :figclass: margin
+  :width: 60%
+  :alt: ModOpt
+  :target: https://cea-cosmic.github.io/ModOpt
+
+- |link-to-sparse2d|: a collection of sparse image transforms written in C++
+- |link-to-modopt|: a library of modular optimisation algorithms
+- Application-specific plug-ins
+
+PySAP provides Python bindings to the C++ libraries, shared tools and a common
+interface for all of the plug-ins.
+
+Plug-ins
+--------
+
+PySAP plug-ins are application-specific tools designed for a given imaging
+domain. Plug-ins combine ModOpt algorithms with image transforms to solve
+complex inverse problems.
+
+.. note::
+  :class: margin
+
+  New plug-ins can be developed using the PySAP |link-to-template|.
+
+The plug-ins currently available in PySAP are:
+
+- `PySAP-Astro <astro.html>`_: astrophysical image processing
+- `PySAP-ETomo <etomo.html>`_: electron tomography image processing
+- `PySAP-MRI <mri.html>`_: magnetic resonance image processing
+
+
+
+Contributors
+------------
+
+You can find a |link-to-contributors|.
+
+.. |link-to-cosmic| raw:: html
+
+  <a href="http://cosmic.cosmostat.org/" target="_blank">COSMIC</a>
+
+.. |link-to-neurospin| raw:: html
+
+  <a href="https://joliot.cea.fr/drf/joliot/en/Pages/research_entities/NeuroSpin.aspx"
+  target="_blank">NeuroSpin</a>
+
+.. |link-to-cosmostat| raw:: html
+
+  <a href="http://www.cosmostat.org/"
+  target="_blank">CosmoStat</a>
+
+.. |link-to-sparse2d| raw:: html
+
+  <a href="https://github.com/CosmoStat/Sparse2D"
+  target="_blank">Sparse2D</a>
+
+.. |link-to-modopt| raw:: html
+
+  <a href="https://cea-cosmic.github.io/ModOpt"
+  target="_blank">ModOpt</a>
+
+.. |link-to-template| raw:: html
+
+  <a href="https://github.com/CEA-COSMIC/pysap-extplugin"
+  target="_blank">plug-in template</a>
+
+.. |link-to-contributors| raw:: html
+
+  <a href="https://github.com/CEA-COSMIC/pysap/graphs/contributors"
+  target="_blank">list of PySAP contributors here</a>
diff --git a/docs/source/citing.rst b/docs/source/citing.rst
new file mode 100644
index 00000000..e719d9d4
--- /dev/null
+++ b/docs/source/citing.rst
@@ -0,0 +1,33 @@
+Citing this Package
+===================
+
+We kindly request that any academic work making use of this package to cite
+:cite:t:`farrens:2020`.
+
+.. bibliography:: my_ref.bib
+
+.. code:: bibtex
+
+  @ARTICLE{2020A&C....3200402F,
+         author = {{Farrens}, S. and {Grigis}, A. and {El Gueddari}, L.
+                   and {Ramzi}, Z. and {Chaithya}, G.~R. and {Starck}, S.
+                   and {Sarthou}, B. and {Cherkaoui}, H. and {Ciuciu}, P.
+                   and {Starck}, J. -L.},
+          title = "{PySAP: Python Sparse Data Analysis Package for
+                    multidisciplinary image processing}",
+        journal = {Astronomy and Computing},
+       keywords = {Image processing, Convex optimisation, Reconstruction,
+                   Open-source software, Astrophysics - Instrumentation and
+                   Methods for Astrophysics},
+           year = 2020,
+          month = jul,
+         volume = {32},
+            eid = {100402},
+          pages = {100402},
+            doi = {10.1016/j.ascom.2020.100402},
+  archivePrefix = {arXiv},
+         eprint = {1910.08465},
+   primaryClass = {astro-ph.IM},
+         adsurl = {https://ui.adsabs.harvard.edu/abs/2020A&C....3200402F},
+        adsnote = {Provided by the SAO/NASA Astrophysics Data System}
+  }
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 00000000..0bf5a1eb
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+# Python Template sphinx config
+
+# Import relevant modules
+import sys
+import os
+from importlib_metadata import metadata
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.insert(0, os.path.abspath('../..'))
+
+# -- General configuration ------------------------------------------------
+
+# General information about the project.
+project = 'pysap'
+
+mdata = metadata(f'python-{project}')
+author = mdata['Author']
+version = mdata['Version']
+copyright = f'2022, {author}'
+gh_user = 'CEA-COSMIC'
+
+# If your documentation needs a minimal Sphinx version, state it here.
+needs_sphinx = '3.3'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.autosummary',
+    'sphinx.ext.coverage',
+    'sphinx.ext.doctest',
+    'sphinx.ext.ifconfig',
+    'sphinx.ext.intersphinx',
+    'sphinx.ext.mathjax',
+    'sphinx.ext.napoleon',
+    'sphinx.ext.todo',
+    'sphinx.ext.viewcode',
+    'sphinx_copybutton',
+    'sphinx_gallery.gen_gallery',
+    'sphinxcontrib.bibtex',
+    'numpydoc',
+]
+
+# Include module names for objects
+add_module_names = False
+
+# Set class documentation standard.
+autoclass_content = 'class'
+
+# Audodoc options
+autodoc_default_options = {
+    'member-order': 'bysource',
+    'private-members': True,
+    'show-inheritance': True
+}
+
+# Generate summaries
+autosummary_generate = True
+
+# Suppress class members in toctree.
+numpydoc_show_class_members = False
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+source_suffix = ['.rst', '.md']
+
+# The master toctree document.
+master_doc = 'index'
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+show_authors = True
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'default'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = True
+
+# Sphinx Gallery
+sphinx_gallery_conf = {
+    'examples_dirs': '../../examples',
+    'gallery_dirs': 'auto_examples',
+    'download_all_examples': False,
+    'show_signature': False,
+    'image_srcset': ["2x"],
+    'reference_url': {
+        'pysap': None,
+        'astro': None,
+        'etomo': None,
+        'mri': None
+    },
+    'compress_images': ('images', 'thumbnails'),
+}
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'sphinx_book_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+html_theme_options = {
+    'repository_url': 'https://github.com/CEA-COSMIC/pysap',
+    'repository_branch': 'develop',
+    'use_issues_button': True,
+    'use_download_button': False,
+    'use_repository_button': True,
+    'use_edit_page_button': True,
+    'path_to_docs': 'docs/source',
+    'home_page_in_toc': True,
+    'logo_only': True,
+    'home_page_in_toc': False,
+    'extra_navbar': "<p></p>",
+}
+html_collapsible_definitions = True
+html_awesome_headerlinks = True
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+html_title = f'{project} v{version}'
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+html_logo = '../images/logo.png'
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+# html_favicon = None
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%d %b, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+# html_use_smartypants = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+html_show_sphinx = False
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+html_show_copyright = True
+
+html_static_path = ['_static']
+html_css_files = ['custom.css']
+
+# -- Intersphinx Mapping ----------------------------------------------
+
+# Refer to the package libraries for type definitions
+intersphinx_mapping = {
+    'python': ('http://docs.python.org/3', None),
+    'numpy': ('https://numpy.org/doc/stable/', None),
+    'scipy': ('https://docs.scipy.org/doc/scipy/reference', None),
+    'progressbar': ('https://progressbar-2.readthedocs.io/en/latest/', None),
+    'matplotlib': ('https://matplotlib.org', None),
+    'astropy': ('http://docs.astropy.org/en/latest/', None),
+    'cupy': ('https://docs-cupy.chainer.org/en/stable/', None),
+    'torch': ('https://pytorch.org/docs/stable/', None),
+    'sklearn': (
+        'http://scikit-learn.org/stable',
+        (None, './_intersphinx/sklearn-objects.inv')
+    ),
+    'tensorflow': (
+        'https://www.tensorflow.org/api_docs/python',
+        (
+            'https://github.com/GPflow/tensorflow-intersphinx/'
+            + 'raw/master/tf2_py_objects.inv')
+    ),
+    'modopt': ('https://cea-cosmic.github.io/ModOpt/', None),
+
+}
+
+# -- BibTeX Setting  ----------------------------------------------
+
+bibtex_bibfiles = ['refs.bib', 'my_ref.bib']
+bibtex_default_style = 'plain'
+bibtex_reference_style = 'author_year'
diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst
new file mode 100644
index 00000000..1835184a
--- /dev/null
+++ b/docs/source/contributing.rst
@@ -0,0 +1,17 @@
+Contributing
+============
+
+PySAP is fully open source and as such we welcome external contributions.
+Please read our |link-to-contrib| for details on how to contribute to the
+development of this package.
+
+All contributors are kindly asked to adhere to the |link-to-conduct|
+at all times to ensure a safe and inclusive environment for everyone.
+
+.. |link-to-contrib| raw:: html
+
+  <a href="https://github.com/cea-cosmic/pysap/blob/master/CONTRIBUTING.md" target="_blank">Contribution Guidelines</a>
+
+.. |link-to-conduct| raw:: html
+
+  <a href="https://github.com/cea-cosmic/pysap/blob/master/CODE_OF_CONDUCT.md" target="_blank">Code of Conduct</a>
diff --git a/docs/source/dependencies.rst b/docs/source/dependencies.rst
new file mode 100644
index 00000000..b2681c70
--- /dev/null
+++ b/docs/source/dependencies.rst
@@ -0,0 +1,54 @@
+Dependencies
+============
+
+.. attention::
+  :class: margin
+
+  PySAP will automatically install all of the required Python dependencies,
+  however issues may arise on some operating systems. If you encounter any
+  problems please ensure that you have all of the listed dependencies
+  installed before opening a new issue.
+
+.. tip::
+  :class: margin
+
+  All of the required dependencies can also be installed using the Conda
+  environment provided.
+
+  .. code:: bash
+
+    conda env create -f environment.yml
+    conda activate pysap
+
+PySAP requires the following dependencies in order to
+`install <installation.html>`_ and run correctly.
+
+1. A C/C++ compiler that supports |link-to-omp|
+2. |link-to-cmake| [>=3.0]
+3. The latest version of |link-to-modopt|
+4. The following third-party Python packages:
+
+   - astropy [>=4.1]
+   - matplotlib [>=3.3.4]
+   - nibabel [>=3.2.1]
+   - numpy [>=1.19.5]
+   - scipy [>=1.5.4]
+   - scikit-learn [>=0.24.1]
+   - progressbar2 [>=3.53.1]
+   - pybind11 [==2.6.2]
+   - pyqt5 [==5.15.4]
+   - pyqtgraph [>=0.11.1]
+   - PyWavelets [>=1.1.1]
+
+
+.. |link-to-omp| raw:: html
+
+ <a href="https://www.openmp.org/" target="_blank">OpenMP</a>
+
+.. |link-to-cmake| raw:: html
+
+ <a href="https://cmake.org/" target="_blank">CMake</a>
+
+.. |link-to-modopt| raw:: html
+
+  <a href="https://cea-cosmic.github.io/ModOpt" target="_blank">ModOpt</a>
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 00000000..6a86ae00
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,102 @@
+.. PySAP logo
+.. figure:: ../images/logo.png
+  :figclass: full-width
+  :width: 50%
+  :alt: PySAP logo
+
+.. PySAP badges
+.. compound::
+  :class: full-width
+
+  .. centered:: |CI|_ |Codecov|_ |PyPi|_ |Docker|_ |arxiv|_
+
+.. Include table of contents
+.. include:: toc.rst
+
+.. Blank line
+|
+
+.. Basic info
+PySAP
+=====
+
+.. topic:: Latest release
+  :class: full-width
+
+  :Author: The PySAP Team
+  :Version: |link-to-pypi|
+  :Tag: |link-to-tag|
+  :Release Date: 18/01/2022
+  :Repository: |link-to-repo|
+
+.. |link-to-pypi| raw:: html
+
+  <a href="https://pypi.org/project/python-PySAP/0.0.6/"
+  target="_blank">0.0.6</a>
+
+.. |link-to-tag| raw:: html
+
+  <a href="https://github.com/CEA-COSMIC/pysap/releases/tag/v0.0.6"
+  target="_blank">v0.0.6</a>
+
+.. |link-to-repo| raw:: html
+
+  <a href="https://github.com/CEA-COSMIC/pysap"
+  target="_blank">github.com/CEA-COSMIC/pysap</a>
+
+.. Blank line
+|
+
+.. PySAP plug-ins
+
+.. rubric:: PySAP Plug-ins
+  :class: full-width
+
+.. compound::
+  :class: full-width
+
+  .. centered:: |pysap-astro-image| |blank-space| |pysap-etomo-image| |blank-space| |pysap-mri-image|
+
+.. |pysap-astro-image| image:: ../images/banner-astro.jpg
+  :width: 30%
+  :alt: PySAP-Astro
+  :target: astro.html
+
+.. |pysap-etomo-image| image:: ../images/banner-etomo.jpg
+  :width: 30%
+  :alt: PySAP-ETomo
+  :target: etomo.html
+
+.. |pysap-mri-image| image:: ../images/banner-mri.jpg
+  :width: 30%
+  :alt: PySAP-MRI
+  :target: mri.html
+
+.. |blank-space| raw:: html
+
+  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+
+.. seealso::
+  :class: full-width
+
+  See our gallery of `examples <auto_examples/index.html>`_ for specific
+  applications of PySAP.
+
+.. Blank line
+|
+
+.. PySAP badge links
+.. |CI| image:: https://github.com/CEA-COSMIC/pysap/workflows/CI/badge.svg
+.. _CI: https://github.com/CEA-COSMIC/pysap/actions?query=workflow%3ACI
+
+.. |Codecov| image:: https://codecov.io/gh/CEA-COSMIC/pysap/branch/master/graph/badge.svg?token=XHJIQXV7AX
+.. _Codecov: https://codecov.io/gh/CEA-COSMIC/pysap
+
+.. |PyPi| image:: https://badge.fury.io/py/python-pySAP.svg
+.. _PyPi: https://badge.fury.io/py/python-pySAP
+
+.. |Docker| image:: https://img.shields.io/docker/cloud/build/ceacosmic/pysap
+.. _Docker: https://hub.docker.com/r/ceacosmic/pysap
+
+.. |arxiv| image:: https://img.shields.io/badge/arXiv-1910.08465-%23b31b1c
+.. _arxiv: https://arxiv.org/abs/1910.08465
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
new file mode 100644
index 00000000..2331d6a4
--- /dev/null
+++ b/docs/source/installation.rst
@@ -0,0 +1,246 @@
+Installation
+============
+
+.. attention::
+
+  The installation of PySAP has been extensively tested on Ubuntu and macOS,
+  however we cannot guarantee it will work on every operating system. A Docker
+  image is available (`see below <#docker-image>`_) for those unable to install
+  PySAP directly.
+
+  If you encounter any installation issues be sure to go through the following
+  steps before opening a new issue:
+
+  1. Check that that all the `dependencies <dependencies.html>`_ have been
+     correctly installed.
+  2. Read through all of the documentation provided, including the
+     troubleshooting suggestions.
+  3. Check if your problem has already been addressed in a previous |link-to-issues|.
+
+Basic installation
+------------------
+
+You can install the latest release of PySAP from |link-to-pypi| as follows.
+
+.. tip::
+  :class: margin
+
+  Depending on your Python setup you may need to provide the ``--user`` option.
+
+  .. code-block:: bash
+
+    pip install --user python-pysap
+
+.. code-block:: bash
+
+  pip install python-pysap
+
+Developers
+----------
+
+Developers are recommend to clone the repository and build the package locally.
+To build PySAP locally run the following.
+
+.. code-block:: bash
+
+  pip install .
+
+Alternatively, you can also run the following.
+
+.. code-block:: bash
+
+  python setup.py install
+
+Custom installation
+-------------------
+
+.. caution::
+  :class: margin
+
+  These options can also be invoked when installing with pip using the
+  ``--install-option="<OPTION>"`` option. For example,
+
+  .. code-block:: bash
+
+    pip install . --install-option="--noplugins"
+
+  However, this will disable the use of wheels and may take significantly
+  longer to build all of the dependencies. Therefore, when installing PySAP
+  this way, it is recommended to pre-install all the required dependencies or
+  use the Conda environment provided.
+
+The following options can be passed when running ``python setup.py install``:
+
+- ``--noplugins`` : Install PySAP without any plug-ins
+- ``--only=<PLUG-IN NAME>`` : Install PySAP with only the specified plug-in
+  name(s) (comma separated)
+- ``--nosparse2d`` : Install PySAP without building Sparse2D
+
+.. admonition:: Example
+
+  To install PySAP with only the ETomo plug-in and without Sparse2D
+  you would run the following.
+
+  .. code-block:: bash
+
+    python setup.py install --nosparse2d --only=pysap-etomo
+
+
+.. figure:: https://continentcot.ca/blogue/wp-content/uploads/2017/05/logo_conda_RGB.png
+  :figclass: margin
+  :width: 200px
+  :alt: Conda logo
+  :target: https://docs.conda.io/
+
+Conda environment
+-----------------
+
+A a |link-to-conda| ``environment.yml`` file is provided to facilitate the
+installation of the required PySAP dependencies along with some optional
+dependencies that provide additional features. To build the environment run the
+following.
+
+.. code-block:: bash
+
+  conda env create -f environment.yml
+
+Then to activate the environment run the following.
+
+.. code-block:: bash
+
+  conda activate pysap
+
+Finally, install PySAP following the instructions above.
+
+.. figure:: https://www.logo.wine/a/logo/Docker_(software)/Docker_(software)-Logo.wine.svg
+  :figclass: margin
+  :width: 210px
+  :alt: Docker logo
+  :target: https://www.docker.com/
+
+Docker image
+------------
+
+A PySAP |link-to-docker| image is available via |link-to-dockerhub| that
+includes the latest stable version of PySAP pre-installed. To install the image
+run the following.
+
+.. code-block:: bash
+
+  docker pull ceacosmic/pysap
+
+A Jupyter notebook can be launched using the Docker image as a backend and with
+access to the user's current working directory as follows.
+
+.. code-block:: bash
+
+  docker run -p 8888:8888 -v ${PWD}:/home ceacosmic/pysap
+
+Troubleshooting
+---------------
+If you encounter any difficulties installing PySAP we recommend that you
+open a |link-to-issue| and we will do our best to help you.
+
+.. figure:: https://www.logo.wine/a/logo/Linux/Linux-Logo.wine.svg
+  :figclass: margin
+  :width: 120px
+  :alt: Linux logo
+  :target: https://www.linux.org/
+
+Linux
+^^^^^
+
+PySAP is actively supported and developed using the latest versions of Ubuntu
+and CentOS. We are confident that you will be able to install PySAP on these
+distributions if all the `required dependencies <dependencies.html>`_ have been
+installed.
+
+.. figure:: https://www.logo.wine/a/logo/Apple_Inc./Apple_Inc.-Logo.wine.svg
+  :figclass: margin
+  :height: 110px
+  :alt: Apple Inc. logo
+  :target: https://www.apple.com/macos
+
+macOS
+^^^^^
+
+PySAP is actively supported and developed using the latest versions of macOS.
+We are confident that you will be able to install PySAP on most versions of
+this operating system if all the `required dependencies <dependencies.html>`_
+have been installed.
+
+.. important::
+  :class: margin
+
+  macOS developer tools must be installed beforehand.
+
+  .. code:: bash
+
+    xcode-select --install
+
+The easiest way to install CMake and add OpenMP support for Clang on macOS is
+to install the following packages using |link-to-homebrew|.
+
+.. code:: bash
+
+  brew install cmake libomp
+
+Further help with macOS can be found |link-to-macos|.
+
+.. figure:: https://www.logo.wine/a/logo/Microsoft_Windows/Microsoft_Windows-Logo.wine.svg
+  :figclass: margin
+  :height: 150px
+  :alt: Microsoft Windows logo
+  :target: https://www.microsoft.com/en-us/windows
+
+Windows
+^^^^^^^
+
+.. tip::
+  :class: margin
+
+  Windows users are encouraged to use the PySAP Docker image to avoid
+  installation issues.
+
+PySAP is not actively supported or developed on Windows, however some tips are
+provided in |link-to-windows|.
+
+.. |link-to-issues| raw:: html
+
+  <a href="https://github.com/CEA-COSMIC/pysap/issues" target="_blank">issue</a>
+
+.. |link-to-pypi| raw:: html
+
+  <a href="https://pypi.org/project/python-pysap" target="_blank">PyPI</a>
+
+.. |link-to-issue| raw:: html
+
+  <a href="https://github.com/CEA-COSMIC/pysap/issues/new/choose"
+  target="_blank">new issue</a>
+
+.. |link-to-conda| raw:: html
+
+  <a href="https://docs.conda.io/" target="_blank">Conda</a>
+
+.. |link-to-docker| raw:: html
+
+  <a href="https://www.docker.com/" target="_blank">Docker</a>
+
+.. |link-to-dockerhub| raw:: html
+
+  <a href="https://hub.docker.com/repository/docker/ceacosmic/pysap"
+  target="_blank">Docker Hub</a>
+
+.. |link-to-homebrew| raw:: html
+
+  <a href="https://brew.sh/" target="_blank">Homebrew</a>
+
+.. |link-to-macos| raw:: html
+
+  <a href="https://github.com/CEA-COSMIC/pysap/blob/master/doc/macos_install.rst
+  "target="_blank">here</a>
+
+.. |link-to-windows| raw:: html
+
+  <a href="https://gist.github.com/chaithyagr/4104df91fbebf44fce1589e96baa6eda
+  "target="_blank">this Gist</a>
diff --git a/docs/source/my_ref.bib b/docs/source/my_ref.bib
new file mode 100644
index 00000000..d7c6593a
--- /dev/null
+++ b/docs/source/my_ref.bib
@@ -0,0 +1,17 @@
+@ARTICLE{farrens:2020,
+       author = {{Farrens}, S. and {Grigis}, A. and {El Gueddari}, L. and {Ramzi}, Z. and {Chaithya}, G.~R. and {Starck}, S. and {Sarthou}, B. and {Cherkaoui}, H. and {Ciuciu}, P. and {Starck}, J. -L.},
+        title = "{PySAP: Python Sparse Data Analysis Package for multidisciplinary image processing}",
+      journal = {Astronomy and Computing},
+     keywords = {Image processing, Convex optimisation, Reconstruction, Open-source software, Astrophysics - Instrumentation and Methods for Astrophysics},
+         year = 2020,
+        month = jul,
+       volume = {32},
+          eid = {100402},
+        pages = {100402},
+          doi = {10.1016/j.ascom.2020.100402},
+archivePrefix = {arXiv},
+       eprint = {1910.08465},
+ primaryClass = {astro-ph.IM},
+       adsurl = {https://ui.adsabs.harvard.edu/abs/2020A&C....3200402F},
+      adsnote = {Provided by the SAO/NASA Astrophysics Data System}
+}
diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst
new file mode 100644
index 00000000..fc301f17
--- /dev/null
+++ b/docs/source/quickstart.rst
@@ -0,0 +1,180 @@
+Quickstart Tutorial
+===================
+
+.. seealso::
+
+  See our gallery of `examples <auto_examples/index.html>`_ for specific
+  applications of PySAP.
+
+Basics
+------
+
+You can import the PySAP package as follows.
+
+.. code-block:: python
+
+  import pysap
+
+Then you can print some basic information about PySAP,
+
+.. code-block:: python
+
+  print(pysap.info())
+
+which should look something like this.
+
+.. code-block::
+
+                   .|'''|       /.\      '||'''|,
+                   ||          // \\      ||   ||
+  '||''|, '||  ||` `|'''|,    //...\\     ||...|'
+   ||  ||  `|..||   .   ||   //     \\    ||
+   ||..|'      ||   |...|' .//       \\. .||
+   ||       ,  |'
+  .||        ''
+
+  Package version: 0.0.6
+
+  License: CeCILL-B
+
+  Authors:
+
+  Antoine Grigis <antoine.grigis@cea.fr>
+  Samuel Farrens <samuel.farrens@cea.fr>
+  Jean-Luc Starck <jl.stark@cea.fr>
+  Philippe Ciuciu <philippe.ciuciu@cea.fr>
+
+  Dependencies:
+
+  scipy          : >=1.5.4   - required | 1.7.3     installed
+  numpy          : >=1.19.5  - required | 1.22.1    installed
+  matplotlib     : >=3.3.4   - required | 3.5.1     installed
+  astropy        : >=4.1     - required | 5.0       installed
+  nibabel        : >=3.2.1   - required | 3.2.1     installed
+  pyqtgraph      : >=0.11.1  - required | 0.12.3    installed
+  progressbar2   : >=3.53.1  - required | ?         installed
+  modopt         : >=1.5.1   - required | 1.6.0     installed
+  scikit-learn   : >=0.24.1  - required | ?         installed
+  pywt           : >=1.1.1   - required | 1.2.0     installed
+  pysparse       : >=0.0.1   - required | 0.1.0     installed
+
+Sample data
+-----------
+
+PySAP includes example data from various imaging domains that can be used for
+testing. Sample data sets can downloaded using the
+:py:func:`get_sample_data <pysap.data.get_sample_data>` function.
+
+.. admonition:: Example
+
+  To download an image of galaxy NGC2997 you would run the following.
+
+  .. code:: python
+
+    from pysap.data import get_sample_data
+    galaxy = get_sample_data('astro-ngc2997')
+
+Image objects
+-------------
+
+The principal data type in PySAP are :py:class:`Image <pysap.base.image.Image>`
+objects. These objects include a built-in
+:py:meth:`show <pysap.base.image.Image.show>` method.
+
+.. admonition:: Example
+
+  Running ``type(galaxy)`` with the object from the previous example should
+  give you ``<class 'pysap.base.image.Image'>`` and thus you can directly
+  visualise this object.
+
+  .. code:: python
+
+    galaxy.show()
+
+``Image`` objects can easily be created from
+:py:class:`Numpy arrays <numpy.ndarray>`.
+
+.. admonition:: Example
+
+  To create a PySAP ``Image`` object from a 2D numpy array you would do the
+  following.
+
+  .. code:: python
+
+    import numpy as np
+    from pysap import Image
+    my_array = np.arange(9).reshape((3, 3))
+    my_image = Image(data=my_array)
+
+Transforms
+----------
+
+You can display all of the available data transforms (i.e. wavelets and more)
+using the :py:func:`wavelist <pysap.utils.wavelist>` function.
+
+.. attention::
+  :class: margin
+
+  We plan to provide more detailed information on each of the available
+  transforms in the near future. The keys ``'isap-3d'`` and ``'isap-2d'``
+  from this dictionary correspond to |link-to-sparse2d|, while ``'pywt'``
+  corresponds to |link-to-pywavelet|.
+
+.. code-block:: python
+
+  print(pysap.wavelist())
+
+A given PySAP transform can be loaded using the
+:py:func:`load_transform <pysap.utils.load_transform>` function.
+
+.. admonition:: Example
+
+  To load the isotropic undecimated wavelet (or starlet) transform with ``4``
+  scales and symmetric padding you would do the following.
+
+  .. code:: python
+
+    from pysap import load_transform
+    my_transform = load_transform('BsplineWaveletTransformATrousAlgorithm')(
+        nb_scale=4,
+        padding_mode='symmetric',
+    )
+
+This will create a class instance of the desired transform with parent
+:py:class:`WaveletTransformBase <pysap.base.transform.WaveletTransformBase>`,
+which has the methods
+:py:meth:`analysis <pysap.base.transform.WaveletTransformBase.analysis>` and
+:py:meth:`synthesis <pysap.base.transform.WaveletTransformBase.synthesis>`
+that can be used to transform images into the corresponding domain and back
+again.
+
+.. tip::
+  :class: margin
+
+  Transform objects also have access to the ``Image`` ``show`` method. Try
+  this!
+
+  .. code:: python
+
+    my_transform.show()
+
+.. admonition:: Example
+
+  Using the transform defined in the previous example and and the ``galaxy``
+  ``Image`` object from earlier you can transform this data into the wavelet
+  domain as follows.
+
+  .. code:: python
+
+    my_transform.data = galaxy
+    my_transform.analysis()
+
+
+
+.. |link-to-sparse2d| raw:: html
+
+  <a href="https://github.com/CosmoStat/Sparse2D" target="_blank">Sparse2D</a>
+
+.. |link-to-pywavelet| raw:: html
+
+  <a href="https://pywavelets.readthedocs.io/ "target="_blank">PyWavelets</a>
diff --git a/docs/source/refs.bib b/docs/source/refs.bib
new file mode 100644
index 00000000..e69de29b
diff --git a/docs/source/toc.rst b/docs/source/toc.rst
new file mode 100644
index 00000000..6098ef79
--- /dev/null
+++ b/docs/source/toc.rst
@@ -0,0 +1,45 @@
+.. Toctrees define sidebar contents
+
+.. toctree::
+  :hidden:
+  :titlesonly:
+  :caption: Getting Started
+
+  about
+  installation
+  dependencies
+  quickstart
+
+.. toctree::
+   :hidden:
+   :titlesonly:
+   :caption: API Documentation
+
+   pysap
+   astro
+   etomo
+   mri
+   z_ref
+
+.. toctree::
+  :hidden:
+  :titlesonly:
+  :caption: Examples
+
+  auto_examples/index
+
+.. toctree::
+  :hidden:
+  :titlesonly:
+  :caption: Related Projects
+
+  ModOpt <https://cea-cosmic.github.io/ModOpt/>
+  Sparse2D <https://github.com/CosmoStat/Sparse2D>
+
+.. toctree::
+  :hidden:
+  :titlesonly:
+  :caption: Guidelines
+
+  contributing
+  citing
diff --git a/docs/source/z_ref.rst b/docs/source/z_ref.rst
new file mode 100644
index 00000000..f2153d73
--- /dev/null
+++ b/docs/source/z_ref.rst
@@ -0,0 +1,4 @@
+References
+==========
+
+.. bibliography:: refs.bib
diff --git a/environment.yml b/environment.yml
index 154b78aa..dde3a6fb 100644
--- a/environment.yml
+++ b/environment.yml
@@ -3,8 +3,9 @@ channels:
   - conda-forge
   - astra-toolbox
 dependencies:
-  - python=3.9
+  - python=3.10
   - astra-toolbox>=2.0.0
+  - cmake>=3.12.0
   - jupyter>=1.0.0
   - matplotlib>=3.3.4
   - pynfft>=1.3.2
@@ -17,7 +18,6 @@ dependencies:
     - nibabel>=3.2.1
     - numpy>=1.19.5
     - progressbar2>=3.53.1
-    - pyqtgraph>=0.11.1
     - PyWavelets>=1.1.1
     - scikit-image>=0.17.2
     - scikit-learn>=0.24.1
diff --git a/examples/README.txt b/examples/README.txt
index 6f2c7d8a..599d11c6 100644
--- a/examples/README.txt
+++ b/examples/README.txt
@@ -1,11 +1,23 @@
-pySAP usage examples
-====================
+.. _examples-index:
 
-.. contents:: **Contents**
-    :local:
-    :depth: 1
+PySAP Examples
+==============
 
-Structure use cases
--------------------
+.. tip::
+  :class: margin
 
-Introductory examples that teach how to use the pysap structure.
+  Each of the examples below can be downloaded as a |link-to-jupyter| notebook
+  which can be run locally.
+
+Here we provide examples of how to run PySAP for various different
+applications.
+
+.. seealso::
+
+  It is recommended that you read through the
+  `installation <../installation.html>`_ instructions and the
+  `quickstart <../quickstart.html>`_ tutorial before running these examples.
+
+.. |link-to-jupyter| raw:: html
+
+  <a href="https://jupyter.org/" target="_blank">Jupyter</a>
diff --git a/examples/astro/README.txt b/examples/astro/README.txt
deleted file mode 100644
index ddbc3ff4..00000000
--- a/examples/astro/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Astro use cases
----------------
diff --git a/examples/astro/galaxy_deconvolution.py b/examples/astro/galaxy_deconvolution.py
deleted file mode 100644
index 13525722..00000000
--- a/examples/astro/galaxy_deconvolution.py
+++ /dev/null
@@ -1,88 +0,0 @@
-"""
-Galaxy Image Deconvolution
-==========================
-
-Credit: S. Farrens
-
-In this tutorial we will deconvolve the PSF effects from an example galaxy
-image.
-
-Import Dependencies
--------------------
-
-Import functions from PySAP and ModOpt.
-
-"""
-
-import numpy as np
-from pysap import Image
-from pysap.data import get_sample_data
-from pysap.plugins.astro.deconvolution.deconvolve import sparse_deconv_condatvu
-from modopt.signal.noise import add_noise
-from modopt.math.convolve import convolve
-
-#############################################################################
-# Load astro data
-# ---------------
-#
-# Load the example images
-
-galaxy = get_sample_data('astro-galaxy')
-psf = get_sample_data('astro-psf')
-
-#############################################################################
-# Show the clean galaxy image
-
-# galaxy.show()
-
-#############################################################################
-# Generate noisy observation
-# --------------------------
-#
-# Convolve the image with a point spread function (PSF) using the `convolve`
-# function. Then add random Gaussian noise with standard deviation 0.0005
-# using the `add_noise` function.
-
-obs_data = add_noise(convolve(galaxy.data, psf.data), sigma=0.0005)
-
-#############################################################################
-# Create a PySAP image object
-
-image_obs = Image(data=np.abs(obs_data))
-
-#############################################################################
-# Show the noisy galaxy image
-
-# image_obs.show()
-
-#############################################################################
-# Deconvolve
-# ----------
-#
-# Use the `sparse_deconv_condatvu` function to deconvolve the noisy image and
-# set the maximum number of iterations to 3000.
-
-deconv_data = sparse_deconv_condatvu(obs_data, psf.data, n_iter=3000)
-
-#############################################################################
-# Create a PySAP image object for the result
-
-image_rec = Image(data=np.abs(deconv_data))
-
-#############################################################################
-# Show the deconvolved galaxy image
-
-# image_rec.show()
-
-#############################################################################
-# Residual
-# --------
-#
-# Create a PySAP image object for the residual
-
-residual = Image(data=np.abs(galaxy.data - deconv_data))
-
-#############################################################################
-# Show the residual
-
-# residual.show()
diff --git a/examples/astro/galaxy_denoising.py b/examples/astro/galaxy_denoising.py
deleted file mode 100644
index d8720583..00000000
--- a/examples/astro/galaxy_denoising.py
+++ /dev/null
@@ -1,83 +0,0 @@
-"""
-Galaxy Image Denoising
-======================
-
-Credit: S. Farrens
-
-In this tutorial we will remove the noise from an example galaxy image.
-
-Import Dependencies
--------------------
-
-Import functions from PySAP and ModOpt.
-
-"""
-
-import numpy as np
-from pysap import Image
-from pysap.data import get_sample_data
-from pysap.plugins.astro.denoising.denoise import denoise
-from modopt.signal.noise import add_noise
-
-#############################################################################
-# Load the image of galaxy NGC2997
-
-galaxy = get_sample_data('astro-ngc2997')
-
-
-#############################################################################
-# Show the clean galaxy image
-
-# galaxy.show()
-
-#############################################################################
-# Generate noisy observation
-# --------------------------
-#
-# Convolve the image with a point spread function (PSF) using the `convolve`
-# function. Then add random Gaussian noise with standard deviation 0.0005
-# using the `add_noise` function.
-
-obs_data = add_noise(galaxy.data, sigma=100)
-
-#############################################################################
-# Create a PySAP image object
-
-image_obs = Image(data=np.abs(obs_data))
-
-#############################################################################
-# Show the noisy galaxy image
-
-# image_obs.show()
-
-#############################################################################
-# Deconvolve
-# ----------
-#
-# Use the `sparse_deconv_condatvu` function to deconvolve the noisy image and
-# set the maximum number of iterations to 3000.
-
-denoise_data = denoise(obs_data, n_scales=4)
-
-#############################################################################
-# Create a PySAP image object for the result
-
-image_rec = Image(data=np.abs(denoise_data))
-
-#############################################################################
-# Show the deconvolved galaxy image
-
-# image_rec.show()
-
-#############################################################################
-# Residual
-# --------
-#
-# Create a PySAP image object for the residual
-
-residual = Image(data=np.abs(galaxy.data - denoise_data))
-
-#############################################################################
-# Show the residual
-
-# residual.show()
diff --git a/examples/mri/CalibrationlessReconstruction_GL_Cartesian.py b/examples/mri/CalibrationlessReconstruction_GL_Cartesian.py
deleted file mode 100644
index 5acd1f0f..00000000
--- a/examples/mri/CalibrationlessReconstruction_GL_Cartesian.py
+++ /dev/null
@@ -1,91 +0,0 @@
-"""
-Cartesian Calibrationless Reconstruction using GroupLASSO Regularizer
-=====================================================================
-
-Author: Chaithya G R
-
-In this tutorial we will reconstruct an MRI image from cartesian kspace
-measurements.
-
-Import neuroimaging data
-------------------------
-
-We use the toy datasets available in pysap, more specifically a 2D parallel MRI
-brain slice on 32 channels and the acquisition cartesian scheme.
-"""
-
-# Package import
-from mri.operators import FFT, WaveletN
-from mri.operators.utils import convert_mask_to_locations
-from mri.reconstructors import CalibrationlessReconstructor
-import pysap
-from pysap.data import get_sample_data
-
-# Third party import
-from modopt.math.metrics import ssim
-from modopt.opt.proximity import GroupLASSO
-import numpy as np
-
-
-# Loading input data
-cartesian_ref_image = get_sample_data('2d-pmri')
-image = pysap.Image(data=np.sqrt(np.sum(cartesian_ref_image.data**2, axis=0)))
-# Obtain MRI cartesian mask
-mask = get_sample_data("cartesian-mri-mask")
-kspace_loc = convert_mask_to_locations(mask.data)
-
-# View Input
-# image.show()
-# mask.show()
-
-#############################################################################
-# Generate the kspace
-# -------------------
-#
-# From the 2D brain slice and the acquisition mask, we retrospectively
-# undersample the k-space using a cartesian acquisition mask
-# We then reconstruct the zero order solution as a baseline
-
-# Get the locations of the kspace samples and the associated observations
-fourier_op = FFT(samples=kspace_loc, shape=image.shape,
-                 n_coils=cartesian_ref_image.shape[0])
-kspace_obs = fourier_op.op(cartesian_ref_image)
-
-# Zero Filled reconstruction
-zero_filled = fourier_op.adj_op(kspace_obs)
-image_rec0 = pysap.Image(data=np.sqrt(np.sum(np.abs(zero_filled)**2, axis=0)))
-# image_rec0.show()
-base_ssim = ssim(image_rec0, image)
-print('The Base SSIM is : ' + str(base_ssim))
-
-#############################################################################
-# FISTA optimization
-# ------------------
-#
-# We now want to refine the zero order solution using a FISTA optimization.
-# The cost function is set to Proximity Cost + Gradient Cost
-
-# Setup the operators
-linear_op = WaveletN(
-    wavelet_name='sym8',
-    nb_scale=4,
-    n_coils=cartesian_ref_image.shape[0],
-)
-regularizer_op = GroupLASSO(weights=6e-8)
-# Setup Reconstructor
-reconstructor = CalibrationlessReconstructor(
-    fourier_op=fourier_op,
-    linear_op=linear_op,
-    regularizer_op=regularizer_op,
-    gradient_formulation='synthesis',
-    verbose=1,
-)
-x_final, costs, metrics = reconstructor.reconstruct(
-    kspace_data=kspace_obs,
-    optimization_alg='fista',
-    num_iterations=300,
-)
-image_rec = pysap.Image(data=np.sqrt(np.sum(np.abs(x_final)**2, axis=0)))
-recon_ssim = ssim(image_rec, image)
-print('The Reconstruction SSIM is : ' + str(recon_ssim))
-# image_rec.show()
diff --git a/examples/mri/CalibrationlessReconstruction_OWL_Cartesian.py b/examples/mri/CalibrationlessReconstruction_OWL_Cartesian.py
deleted file mode 100644
index 7bf10af0..00000000
--- a/examples/mri/CalibrationlessReconstruction_OWL_Cartesian.py
+++ /dev/null
@@ -1,96 +0,0 @@
-"""
-Cartesian Calibrationless Reconstruction using OWL Regularizer
-==============================================================
-
-Author: Chaithya G R
-
-In this tutorial we will reconstruct an MRI image from cartesian kspace
-measurements.
-
-Import neuroimaging data
-------------------------
-
-We use the toy datasets available in pysap, more specifically a 2D parallel MRI
-brain slice on 32 channels and the acquisition cartesian scheme.
-"""
-
-# Package import
-from mri.operators import FFT, WaveletN, OWL
-from mri.operators.utils import convert_mask_to_locations
-from mri.reconstructors import CalibrationlessReconstructor
-import pysap
-from pysap.data import get_sample_data
-
-# Third party import
-from modopt.math.metrics import ssim
-import numpy as np
-
-
-# Loading input data
-cartesian_ref_image = get_sample_data('2d-pmri')
-image = pysap.Image(data=np.sqrt(np.sum(cartesian_ref_image.data**2, axis=0)))
-# Obtain MRI cartesian mask
-mask = get_sample_data("cartesian-mri-mask")
-kspace_loc = convert_mask_to_locations(mask.data)
-
-# View Input
-# image.show()
-# mask.show()
-
-#############################################################################
-# Generate the kspace
-# -------------------
-#
-# From the 2D brain slice and the acquisition mask, we retrospectively
-# undersample the k-space using a cartesian acquisition mask
-# We then reconstruct the zero order solution as a baseline
-
-# Get the locations of the kspace samples and the associated observations
-fourier_op = FFT(samples=kspace_loc, shape=image.shape,
-                 n_coils=cartesian_ref_image.shape[0])
-kspace_obs = fourier_op.op(cartesian_ref_image)
-
-# Zero Filled reconstruction
-zero_filled = fourier_op.adj_op(kspace_obs)
-image_rec0 = pysap.Image(data=np.sqrt(np.sum(np.abs(zero_filled)**2, axis=0)))
-# image_rec0.show()
-base_ssim = ssim(image_rec0, image)
-print('The Base SSIM is : ' + str(base_ssim))
-
-#############################################################################
-# FISTA optimization
-# ------------------
-#
-# We now want to refine the zero order solution using a FISTA optimization.
-# The cost function is set to Proximity Cost + Gradient Cost
-
-# Setup the operators
-linear_op = WaveletN(
-    wavelet_name='sym8',
-    nb_scale=4,
-    n_coils=cartesian_ref_image.shape[0],
-)
-coeffs = linear_op.op(cartesian_ref_image)
-regularizer_op = OWL(
-    alpha=1.05e-8,
-    beta=0,
-    mode='band_based',
-    n_coils=cartesian_ref_image.shape[0],
-    bands_shape=linear_op.coeffs_shape,
-)
-# Setup Reconstructor
-reconstructor = CalibrationlessReconstructor(
-    fourier_op=fourier_op,
-    linear_op=linear_op,
-    regularizer_op=regularizer_op,
-    gradient_formulation='synthesis',
-    verbose=1,
-)
-x_final, costs, metrics = reconstructor.reconstruct(
-    kspace_data=kspace_obs,
-    optimization_alg='fista',
-    num_iterations=200,
-)
-image_rec = pysap.Image(data=np.sqrt(np.sum(np.abs(x_final)**2, axis=0)))
-recon_ssim = ssim(image_rec, image)
-print('The Reconstruction SSIM is : ' + str(recon_ssim))
diff --git a/examples/mri/GPU_Examples/NonCartesian_gpuNUFFT_DensityCompensation.py b/examples/mri/GPU_Examples/NonCartesian_gpuNUFFT_DensityCompensation.py
deleted file mode 100644
index 2f219c70..00000000
--- a/examples/mri/GPU_Examples/NonCartesian_gpuNUFFT_DensityCompensation.py
+++ /dev/null
@@ -1,78 +0,0 @@
-"""
-Neuroimaging non-cartesian reconstruction
-=========================================
-
-Author: Chaithya G R
-
-In this tutorial we will reconstruct an MR Image directly with density
-compensation
-
-Import neuroimaging data
-------------------------
-
-We use the toy datasets available in pysap, more specifically a 2D brain slice
-and the radial acquisition scheme (non-cartesian).
-"""
-
-# Package import
-from mri.operators import NonCartesianFFT, WaveletUD2
-from mri.operators.utils import convert_locations_to_mask, \
-    gridded_inverse_fourier_transform_nd
-from mri.operators.fourier.utils import estimate_density_compensation
-from mri.reconstructors import SingleChannelReconstructor
-import pysap
-from pysap.data import get_sample_data
-
-# Third party import
-from modopt.math.metrics import ssim
-from modopt.opt.linear import Identity
-from modopt.opt.proximity import SparseThreshold
-import numpy as np
-
-# Loading input data
-image = get_sample_data('2d-mri')
-
-# Obtain MRI non-cartesian mask and estimate the density compensation
-radial_mask = get_sample_data("mri-radial-samples")
-kspace_loc = radial_mask.data
-density_comp = estimate_density_compensation(kspace_loc, image.shape)
-
-#############################################################################
-# Generate the kspace
-# -------------------
-#
-# From the 2D brain slice and the acquisition mask, we retrospectively
-# undersample the k-space using a radial acquisition mask
-# We then reconstruct using adjoint with and without density compensation
-
-# Get the locations of the kspace samples and the associated observations
-fourier_op = NonCartesianFFT(
-    samples=kspace_loc,
-    shape=image.shape,
-    implementation='gpuNUFFT',
-)
-fourier_op_density_comp = NonCartesianFFT(
-    samples=kspace_loc,
-    shape=image.shape,
-    implementation='gpuNUFFT',
-    density_comp=density_comp
-)
-# Get the kspace data retrospectively. Note that this can be done with
-# `fourier_op_density_comp` as the forward operator is the same
-kspace_obs = fourier_op.op(image.data)
-
-# Simple adjoint
-image_rec0 = pysap.Image(data=np.abs(fourier_op.adj_op(kspace_obs)))
-# image_rec0.show()
-base_ssim = ssim(image_rec0, image)
-print('The SSIM from Adjoint is : ' + str(base_ssim))
-
-# Density Compensation adjoint:
-# This preconditions k-space giving a result closer to inverse
-image_rec1 = pysap.Image(data=np.abs(
-    fourier_op_density_comp.adj_op(kspace_obs))
-)
-# image_rec1.show()
-new_ssim = ssim(image_rec1, image)
-print('The SSIM from Density '
-      'compensated Adjoint is : ' + str(new_ssim))
diff --git a/examples/mri/GPU_Examples/NonCartesian_gpuNUFFT_SENSE_DensityCompensation.py b/examples/mri/GPU_Examples/NonCartesian_gpuNUFFT_SENSE_DensityCompensation.py
deleted file mode 100644
index 702272bc..00000000
--- a/examples/mri/GPU_Examples/NonCartesian_gpuNUFFT_SENSE_DensityCompensation.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""
-Neuroimaging non-cartesian reconstruction
-=========================================
-
-Author: Chaithya G R
-
-In this tutorial we will reconstruct an MR Image directly with density
-compensation and SENSE from gpuNUFFT
-
-Import neuroimaging data
-------------------------
-
-We use the toy datasets available in pysap, more specifically a 3D orange data
-and the radial acquisition scheme (non-cartesian).
-"""
-
-# Package import
-from mri.operators import NonCartesianFFT, WaveletUD2
-from mri.operators.utils import convert_locations_to_mask, \
-    gridded_inverse_fourier_transform_nd
-from mri.operators.fourier.utils import estimate_density_compensation
-from mri.reconstructors import SingleChannelReconstructor
-from mri.reconstructors.utils.extract_sensitivity_maps import get_Smaps
-import pysap
-from pysap.data import get_sample_data
-
-# Third party import
-from modopt.math.metrics import ssim
-from modopt.opt.linear import Identity
-from modopt.opt.proximity import SparseThreshold
-import numpy as np
-
-# Loading input data
-image = get_sample_data('3d-pmri')
-cartesian = np.linalg.norm(image, axis=0)
-
-# Obtain MRI non-cartesian mask and estimate the density compensation
-radial_mask = get_sample_data("mri-radial-3d-samples")
-kspace_loc = radial_mask.data
-density_comp = estimate_density_compensation(kspace_loc, cartesian.shape)
-
-#############################################################################
-# Generate the kspace
-# -------------------
-#
-# From the 3D orange slice and 3D radial acquisition mask, we retrospectively
-# undersample the k-space
-# We then reconstruct using adjoint with and without density compensation
-
-# Get the locations of the kspace samples and the associated observations
-fourier_op = NonCartesianFFT(
-    samples=kspace_loc,
-    shape=cartesian.shape,
-    n_coils=image.shape[0],
-    implementation='gpuNUFFT',
-)
-kspace_obs = fourier_op.op(image.data)
-Smaps, SOS = get_Smaps(
-    k_space=kspace_obs,
-    img_shape=fourier_op.shape,
-    samples=kspace_loc,
-    thresh=(0.05, 0.05, 0.05),  # The cutoff threshold in each kspace
-                                # direction between 0 and kspace_max (0.5)
-    min_samples=kspace_loc.min(axis=0),
-    max_samples=kspace_loc.max(axis=0),
-    density_comp=density_comp,
-    mode='NFFT',
-)
-fourier_op_sense_dc = NonCartesianFFT(
-    samples=kspace_loc,
-    shape=cartesian.shape,
-    implementation='gpuNUFFT',
-    n_coils=image.shape[0],
-    density_comp=density_comp,
-    smaps=Smaps,
-)
-
-# Density Compensation SENSE adjoint:
-# This preconditions k-space giving a result closer to inverse
-image_rec1 = pysap.Image(data=np.abs(
-    fourier_op_sense_dc.adj_op(kspace_obs))
-)
-# image_rec1.show()
-base_ssim = ssim(image_rec1, cartesian)
-print('The SSIM for simple Density '
-      'compensated SENSE Adjoint is : ' + str(base_ssim))
diff --git a/examples/mri/GPU_Examples/SelfCalibrationReconstruction_NonCartesian_gpuNUFFT_SENSE.py b/examples/mri/GPU_Examples/SelfCalibrationReconstruction_NonCartesian_gpuNUFFT_SENSE.py
deleted file mode 100644
index d7fccaf7..00000000
--- a/examples/mri/GPU_Examples/SelfCalibrationReconstruction_NonCartesian_gpuNUFFT_SENSE.py
+++ /dev/null
@@ -1,128 +0,0 @@
-"""
-Non Cartesian Self Calibrating Reconstruction
-=============================================
-
-Author: Chaithya G R
-
-In this tutorial we will reconstruct an MRI image from non cartesian kspace
-measurements. We will use gpuNUFFT and SENSE Reconstruction on GPU
-
-Import neuroimaging data
-------------------------
-
-We use the toy datasets available in pysap, more specifically a 2D parallel MRI
-brain slice on 32 channels and the acquisition using non cartesian radial
-samples.
-"""
-
-# Package import
-from mri.operators import NonCartesianFFT, WaveletN
-from mri.reconstructors import SelfCalibrationReconstructor
-from mri.reconstructors.utils.extract_sensitivity_maps import get_Smaps
-from mri.operators.utils import convert_locations_to_mask, \
-    gridded_inverse_fourier_transform_nd
-import pysap
-from pysap.data import get_sample_data
-
-# Third party import
-from modopt.math.metrics import ssim
-from modopt.opt.linear import Identity
-from modopt.opt.proximity import SparseThreshold
-import numpy as np
-
-# Loading input data
-cartesian_ref_image = get_sample_data('2d-pmri')
-image = pysap.Image(data=np.sqrt(np.sum(cartesian_ref_image.data**2, axis=0)))
-
-# Obtain MRI cartesian mask
-mask = get_sample_data("mri-radial-samples")
-kspace_loc = mask.data
-
-# View Input
-# image.show()
-# mask.show()
-
-#############################################################################
-# Generate the kspace
-# -------------------
-#
-# From the 2D brain slice and the acquisition mask, we retrospectively
-# undersample the k-space using a non cartesian acquisition mask
-# We then grid the kspace to get the gridded solution as a baseline
-
-# Get the kspace observation values for the kspace locations
-fourier_op = NonCartesianFFT(
-    samples=kspace_loc,
-    shape=image.shape,
-    n_coils=cartesian_ref_image.shape[0],
-    implementation='gpuNUFFT'
-)
-kspace_obs = fourier_op.op(cartesian_ref_image)
-
-# Gridded solution
-grid_space = np.linspace(-0.5, 0.5, num=image.shape[0])
-grid2D = np.meshgrid(grid_space, grid_space)
-grid_soln = np.asarray([
-    gridded_inverse_fourier_transform_nd(kspace_loc, kspace_obs_ch,
-                                         tuple(grid2D), 'linear')
-    for kspace_obs_ch in kspace_obs
-])
-image_rec0 = pysap.Image(data=np.sqrt(np.sum(np.abs(grid_soln)**2, axis=0)))
-# image_rec0.show()
-base_ssim = ssim(image_rec0, image)
-print('The Base SSIM is : ' + str(base_ssim))
-
-# Obtain SMaps
-Smaps, SOS = get_Smaps(
-    k_space=kspace_obs,
-    img_shape=fourier_op.shape,
-    samples=kspace_loc,
-    thresh=(0.01, 0.01),    # The cutoff threshold in each kspace direction
-                            # between 0 and kspace_max (0.5)
-    min_samples=kspace_loc.min(axis=0),
-    max_samples=kspace_loc.max(axis=0),
-    mode='gridding',
-    method='linear',
-    n_cpu=-1,
-)
-# Setup Fourier Operator with SENSE. This would initialize the
-# fourier operators in the GPU.
-# For this we need to specify the implementation as gpuNUFFT
-# and also pass the Smaps calculated above
-fourier_op_sense = NonCartesianFFT(
-    samples=kspace_loc,
-    shape=image.shape,
-    n_coils=cartesian_ref_image.shape[0],
-    smaps=Smaps,
-    implementation='gpuNUFFT',
-)
-
-#############################################################################
-# FISTA optimization
-# ------------------
-#
-# We now want to refine the zero order solution using a FISTA optimization.
-# The cost function is set to Proximity Cost + Gradient Cost
-
-# Setup the operators
-linear_op = WaveletN(
-    wavelet_name='sym8',
-    nb_scale=4,
-)
-regularizer_op = SparseThreshold(Identity(), 3 * 1e-9, thresh_type="soft")
-# Setup Reconstructor
-reconstructor = SelfCalibrationReconstructor(
-    fourier_op=fourier_op_sense,
-    linear_op=linear_op,
-    regularizer_op=regularizer_op,
-    gradient_formulation='synthesis',
-    verbose=1,
-)
-x_final, costs, metrics = reconstructor.reconstruct(
-    kspace_data=kspace_obs,
-    optimization_alg='fista',
-    num_iterations=200,
-)
-image_rec = pysap.Image(data=x_final)
-recon_ssim = ssim(image_rec, image)
-print('The Reconstruction SSIM is : ' + str(recon_ssim))
diff --git a/examples/mri/README.txt b/examples/mri/README.txt
deleted file mode 100644
index 876bd2b9..00000000
--- a/examples/mri/README.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-Usage examples
-==============
-
-.. contents:: **Contents**
-    :local:
-    :depth: 2
diff --git a/examples/mri/SelfCalibrationReconstruction_Cartesian.py b/examples/mri/SelfCalibrationReconstruction_Cartesian.py
deleted file mode 100644
index bfa41ccd..00000000
--- a/examples/mri/SelfCalibrationReconstruction_Cartesian.py
+++ /dev/null
@@ -1,91 +0,0 @@
-"""
-Cartesian Self Calibrating Reconstruction
-=========================================
-
-Author: Chaithya G R
-
-In this tutorial we will reconstruct an MRI image from cartesian kspace
-measurements.
-
-Import neuroimaging data
-------------------------
-
-We use the toy datasets available in pysap, more specifically a 2D parallel MRI
-brain slice on 32 channels and the acquisition cartesian scheme.
-"""
-
-# Package import
-from mri.operators import FFT, WaveletN
-from mri.operators.utils import convert_mask_to_locations
-from mri.reconstructors import SelfCalibrationReconstructor
-import pysap
-from pysap.data import get_sample_data
-
-# Third party import
-from modopt.math.metrics import ssim
-from modopt.opt.linear import Identity
-from modopt.opt.proximity import SparseThreshold
-import numpy as np
-
-
-# Loading input data
-cartesian_ref_image = get_sample_data('2d-pmri')
-image = pysap.Image(data=np.sqrt(np.sum(cartesian_ref_image.data**2, axis=0)))
-# Obtain MRI cartesian mask
-mask = get_sample_data("cartesian-mri-mask")
-kspace_loc = convert_mask_to_locations(mask.data)
-
-# View Input
-# image.show()
-# mask.show()
-
-#############################################################################
-# Generate the kspace
-# -------------------
-#
-# From the 2D brain slice and the acquisition mask, we retrospectively
-# undersample the k-space using a cartesian acquisition mask
-# We then reconstruct the zero order solution as a baseline
-
-# Get the locations of the kspace samples and the associated observations
-fourier_op = FFT(samples=kspace_loc, shape=image.shape,
-                 n_coils=cartesian_ref_image.shape[0])
-kspace_obs = fourier_op.op(cartesian_ref_image)
-
-# Zero Filled reconstruction
-zero_filled = fourier_op.adj_op(kspace_obs)
-image_rec0 = pysap.Image(data=np.sqrt(np.sum(np.abs(zero_filled)**2, axis=0)))
-# image_rec0.show()
-base_ssim = ssim(image_rec0, image)
-print('The Base SSIM is : ' + str(base_ssim))
-
-#############################################################################
-# FISTA optimization
-# ------------------
-#
-# We now want to refine the zero order solution using a FISTA optimization.
-# The cost function is set to Proximity Cost + Gradient Cost
-
-# Setup the operators
-linear_op = WaveletN(
-    wavelet_name='sym8',
-    nb_scale=4,
-)
-regularizer_op = SparseThreshold(Identity(), 1.5e-8, thresh_type="soft")
-# Setup Reconstructor
-reconstructor = SelfCalibrationReconstructor(
-    fourier_op=fourier_op,
-    linear_op=linear_op,
-    regularizer_op=regularizer_op,
-    gradient_formulation='synthesis',
-    kspace_portion=0.01,
-    verbose=1,
-)
-x_final, costs, metrics = reconstructor.reconstruct(
-    kspace_data=kspace_obs,
-    optimization_alg='fista',
-    num_iterations=10,
-)
-image_rec = pysap.Image(data=x_final)
-recon_ssim = ssim(image_rec, image)
-print('The Reconstruction SSIM is : ' + str(recon_ssim))
diff --git a/examples/mri/cartesian_reconstruction.py b/examples/mri/cartesian_reconstruction.py
deleted file mode 100644
index 6113f978..00000000
--- a/examples/mri/cartesian_reconstruction.py
+++ /dev/null
@@ -1,92 +0,0 @@
-"""
-Neuroimaging cartesian reconstruction
-=====================================
-
-Author: Chaithya G R
-
-In this tutorial we will reconstruct an MRI image from the sparse kspace
-measurements.
-
-Import neuroimaging data
-------------------------
-
-We use the toy datasets available in pysap, more specifically a 2D brain slice
-and the cartesian acquisition scheme.
-"""
-
-# Package import
-from mri.operators import FFT, WaveletN
-from mri.operators.utils import convert_mask_to_locations
-from mri.reconstructors import SingleChannelReconstructor
-import pysap
-from pysap.data import get_sample_data
-
-# Third party import
-from modopt.opt.proximity import SparseThreshold
-from modopt.opt.linear import Identity
-from modopt.math.metrics import ssim
-import numpy as np
-
-# Loading input data
-image = get_sample_data('2d-mri')
-
-# Obtain K-Space Cartesian Mask
-mask = get_sample_data("cartesian-mri-mask")
-
-# View Input
-# image.show()
-# mask.show()
-
-#############################################################################
-# Generate the kspace
-# -------------------
-#
-# From the 2D brain slice and the acquisition mask, we retrospectively
-# undersample the k-space using a cartesian acquisition mask
-# We then reconstruct the zero order solution as a baseline
-
-
-# Get the locations of the kspace samples
-kspace_loc = convert_mask_to_locations(mask.data)
-# Generate the subsampled kspace
-fourier_op = FFT(samples=kspace_loc, shape=image.shape)
-kspace_data = fourier_op.op(image)
-
-# Zero order solution
-image_rec0 = pysap.Image(data=fourier_op.adj_op(kspace_data),
-                         metadata=image.metadata)
-# image_rec0.show()
-
-# Calculate SSIM
-base_ssim = ssim(image_rec0, image)
-print(base_ssim)
-
-#############################################################################
-# FISTA optimization
-# ------------------
-#
-# We now want to refine the zero order solution using a FISTA optimization.
-# The cost function is set to Proximity Cost + Gradient Cost
-
-# Setup the operators
-linear_op = WaveletN(wavelet_name="sym8", nb_scales=4)
-regularizer_op = SparseThreshold(Identity(), 2 * 1e-7, thresh_type="soft")
-# Setup Reconstructor
-reconstructor = SingleChannelReconstructor(
-    fourier_op=fourier_op,
-    linear_op=linear_op,
-    regularizer_op=regularizer_op,
-    gradient_formulation='synthesis',
-    verbose=1,
-)
-# Start Reconstruction
-x_final, costs, metrics = reconstructor.reconstruct(
-    kspace_data=kspace_data,
-    optimization_alg='fista',
-    num_iterations=200,
-)
-image_rec = pysap.Image(data=np.abs(x_final))
-# image_rec.show()
-# Calculate SSIM
-recon_ssim = ssim(image_rec, image)
-print('The Reconstruction SSIM is : ' + str(recon_ssim))
diff --git a/examples/mri/cartesian_reconstruction_3d.py b/examples/mri/cartesian_reconstruction_3d.py
deleted file mode 100644
index ecfcfb4e..00000000
--- a/examples/mri/cartesian_reconstruction_3d.py
+++ /dev/null
@@ -1,100 +0,0 @@
-"""
-3D Neuroimaging cartesian reconstruction
-========================================
-
-Author: LElgueddari
-
-In this tutorial we will reconstruct an MRI image from the sparse 3D kspace
-measurements.
-
-Import neuroimaging data
-------------------------
-
-We use the toy datasets available in pysap, more specifically the 3D orange
-and the cartesian acquisition scheme.
-"""
-
-# Package import
-from modopt.math.metrics import ssim
-from mri.operators import FFT, WaveletN
-from mri.operators.utils import convert_mask_to_locations
-from mri.reconstructors import SingleChannelReconstructor
-from pysap.data import get_sample_data
-import pysap
-
-# Third party import
-from modopt.opt.linear import Identity
-from modopt.opt.proximity import SparseThreshold
-import numpy as np
-
-# Loading input data and convert it into a single channel using Sum-Of-Squares
-image = get_sample_data('3d-pmri')
-image.data = np.sqrt(np.sum(np.abs(image.data)**2, axis=0))
-
-# Obtain K-Space Cartesian Mask
-mask = get_sample_data("2d-poisson-disk-mask")
-mask.data = np.repeat(np.expand_dims(mask.data, axis=-1), image.shape[-1],
-                      axis=-1)
-
-# View Input
-# image.show()
-# mask.show()
-
-#############################################################################
-# Generate the kspace
-# -------------------
-#
-# From the 3D Orange volume and the acquisition mask, we retrospectively
-# undersample the k-space using a cartesian acquisition mask
-# We then reconstruct the zero order solution as a baseline
-
-
-# Get the locations of the kspace samples
-kspace_loc = convert_mask_to_locations(mask.data)
-# Generate the subsampled kspace
-fourier_op = FFT(samples=kspace_loc, shape=image.shape)
-kspace_data = fourier_op.op(image)
-
-# Zero order solution
-image_rec0 = pysap.Image(data=fourier_op.adj_op(kspace_data),
-                         metadata=image.metadata)
-# image_rec0.show()
-
-# Calculate SSIM
-base_ssim = ssim(image_rec0, image)
-print(base_ssim)
-
-#############################################################################
-# FISTA optimization
-# ------------------
-#
-# We now want to refine the zero order solution using a FISTA optimization.
-# The cost function is set to Proximity Cost + Gradient Cost
-
-# Setup the operators
-linear_op = WaveletN(
-    wavelet_name="sym8",
-    nb_scales=4,
-    dim=3,
-    padding_mode="periodization",
-)
-regularizer_op = SparseThreshold(Identity(), 2 * 1e-11, thresh_type="soft")
-# Setup Reconstructor
-reconstructor = SingleChannelReconstructor(
-    fourier_op=fourier_op,
-    linear_op=linear_op,
-    regularizer_op=regularizer_op,
-    gradient_formulation='synthesis',
-    verbose=1,
-)
-# Start Reconstruction
-x_final, costs, metrics = reconstructor.reconstruct(
-    kspace_data=kspace_data,
-    optimization_alg='fista',
-    num_iterations=200,
-)
-image_rec = pysap.Image(data=np.abs(x_final))
-# image_rec.show()
-# Calculate SSIM
-recon_ssim = ssim(image_rec, image)
-print('The Reconstruction SSIM is : ' + str(recon_ssim))
diff --git a/examples/mri/gridsearch_launch.py b/examples/mri/gridsearch_launch.py
deleted file mode 100644
index 3182b8d1..00000000
--- a/examples/mri/gridsearch_launch.py
+++ /dev/null
@@ -1,84 +0,0 @@
-"""
-Neuroimaging cartesian reconstruction
-=====================================
-
-Author: Chaithya G R
-
-In this tutorial we will use the pysap-mri's launch grid helper function
-to carry out grid search. We will search for best regularisation weight
-and the best wavelet for reconstruction.
-For this the search space works on :
-mu          ==> 5 Values on log scale between 1e-8 and 1e-6
-Wavelets    ==> sym8 and sym12
-nb_scale    ==> 3 and 4
-"""
-
-# Imports
-from mri.operators import FFT, WaveletN
-from mri.operators.utils import convert_mask_to_locations
-from mri.reconstructors import SingleChannelReconstructor
-from mri.scripts.gridsearch import launch_grid
-
-from pysap.data import get_sample_data
-
-from modopt.math.metrics import ssim
-from modopt.opt.proximity import SparseThreshold
-from modopt.opt.linear import Identity
-import numpy as np
-
-
-# Load MR data and obtain kspace
-image = get_sample_data('2d-mri')
-mask = get_sample_data("cartesian-mri-mask")
-kspace_loc = convert_mask_to_locations(mask.data)
-fourier_op = FFT(samples=kspace_loc, shape=image.shape)
-kspace_data = fourier_op.op(image.data)
-# Define the keyword dictionaries based on convention
-ref = image
-metrics = {
-    'ssim': {
-        'metric': ssim,
-        'mapping': {'x_new': 'test', 'y_new': None},
-        'cst_kwargs': {'ref': image, 'mask': None},
-        'early_stopping': True,
-    },
-}
-linear_params = {
-    'init_class': WaveletN,
-    'kwargs':
-        {
-            'wavelet_name': ['sym8', 'sym12'],
-            'nb_scale': [3, 4]
-        }
-}
-regularizer_params = {
-    'init_class': SparseThreshold,
-    'kwargs':
-        {
-            'linear': Identity(),
-            'weights': np.logspace(-8, -6, 5),
-        }
-}
-optimizer_params = {
-    # Just following convention
-    'kwargs':
-        {
-            'optimization_alg': 'fista',
-            'num_iterations': 20,
-            'metrics': metrics,
-        }
-}
-# Call the launch grid function and obtain results
-raw_results, test_cases, key_names, best_idx = launch_grid(
-    kspace_data=kspace_data,
-    fourier_op=fourier_op,
-    linear_params=linear_params,
-    regularizer_params=regularizer_params,
-    optimizer_params=optimizer_params,
-    reconstructor_class=SingleChannelReconstructor,
-    reconstructor_kwargs={'gradient_formulation': 'synthesis'},
-    compare_metric_details={'metric': 'ssim'},
-    n_jobs=-1,
-    verbose=1,
-)
-image_rec = raw_results[best_idx][0]
diff --git a/examples/mri/non_cartesian_reconstruction.py b/examples/mri/non_cartesian_reconstruction.py
deleted file mode 100644
index 94df10d3..00000000
--- a/examples/mri/non_cartesian_reconstruction.py
+++ /dev/null
@@ -1,95 +0,0 @@
-"""
-Neuroimaging non-cartesian reconstruction
-=========================================
-
-Author: Chaithya G R
-
-In this tutorial we will reconstruct an MRI image from non-cartesian kspace
-measurements.
-
-Import neuroimaging data
-------------------------
-
-We use the toy datasets available in pysap, more specifically a 2D brain slice
-and the acquisition cartesian scheme.
-"""
-
-# Package import
-from mri.operators import NonCartesianFFT, WaveletUD2
-from mri.operators.utils import convert_locations_to_mask, \
-    gridded_inverse_fourier_transform_nd
-from mri.reconstructors import SingleChannelReconstructor
-import pysap
-from pysap.data import get_sample_data
-
-# Third party import
-from modopt.math.metrics import ssim
-from modopt.opt.linear import Identity
-from modopt.opt.proximity import SparseThreshold
-import numpy as np
-
-# Loading input data
-image = get_sample_data('2d-mri')
-
-# Obtain MRI non-cartesian mask
-radial_mask = get_sample_data("mri-radial-samples")
-kspace_loc = radial_mask.data
-mask = pysap.Image(data=convert_locations_to_mask(kspace_loc, image.shape))
-
-# View Input
-# image.show()
-# mask.show()
-
-#############################################################################
-# Generate the kspace
-# -------------------
-#
-# From the 2D brain slice and the acquisition mask, we retrospectively
-# undersample the k-space using a radial acquisition mask
-# We then reconstruct the zero order solution as a baseline
-
-# Get the locations of the kspace samples and the associated observations
-fourier_op = NonCartesianFFT(samples=kspace_loc, shape=image.shape,
-                             implementation='cpu')
-kspace_obs = fourier_op.op(image.data)
-
-# Gridded solution
-grid_space = np.linspace(-0.5, 0.5, num=image.shape[0])
-grid2D = np.meshgrid(grid_space, grid_space)
-grid_soln = gridded_inverse_fourier_transform_nd(kspace_loc, kspace_obs,
-                                                 tuple(grid2D), 'linear')
-image_rec0 = pysap.Image(data=grid_soln)
-# image_rec0.show()
-base_ssim = ssim(image_rec0, image)
-print('The Base SSIM is : ' + str(base_ssim))
-
-#############################################################################
-# FISTA optimization
-# ------------------
-#
-# We now want to refine the zero order solution using a FISTA optimization.
-# The cost function is set to Proximity Cost + Gradient Cost
-
-# Setup the operators
-linear_op = WaveletUD2(
-    wavelet_id=24,
-    nb_scale=4,
-)
-regularizer_op = SparseThreshold(Identity(), 6 * 1e-7, thresh_type="soft")
-# Setup Reconstructor
-reconstructor = SingleChannelReconstructor(
-    fourier_op=fourier_op,
-    linear_op=linear_op,
-    regularizer_op=regularizer_op,
-    gradient_formulation='synthesis',
-    verbose=1,
-)
-x_final, costs, metrics = reconstructor.reconstruct(
-    kspace_data=kspace_obs,
-    optimization_alg='fista',
-    num_iterations=200,
-)
-image_rec = pysap.Image(data=np.abs(x_final))
-# image_rec.show()
-recon_ssim = ssim(image_rec, image)
-print('The Reconstruction SSIM is : ' + str(recon_ssim))
diff --git a/examples/pysap/README.txt b/examples/pysap/README.txt
new file mode 100644
index 00000000..4fcbf5fc
--- /dev/null
+++ b/examples/pysap/README.txt
@@ -0,0 +1,7 @@
+.. _basic_examples:
+
+Basic examples
+--------------
+
+This section provides some introductory examples that demonstrate how use some
+of the core features of PySAP.
diff --git a/examples/pysap/plot_basics.py b/examples/pysap/plot_basics.py
new file mode 100644
index 00000000..978a16c5
--- /dev/null
+++ b/examples/pysap/plot_basics.py
@@ -0,0 +1,114 @@
+"""
+PySAP Basics
+============
+
+.. codeauthor:: Antoine Grigis <antoine.grigis@cea.fr>
+
+This example introduces some of the basic features of the core PySAP package
+as well as some tests to ensure that everything has been installed correctly.
+
+First checks
+------------
+
+In order to test if the ``pysap`` package is properly installed on your
+machine, you can check the package version.
+
+"""
+
+import matplotlib.pyplot as plt
+import pysap
+
+print(pysap.__version__)
+
+# %%
+# Now you can run the the configuration info function to see if all the
+# dependencies are installed properly:
+
+import pysap.configure
+
+print(pysap.configure.info())
+
+# %%
+# Import astronomical data
+# ------------------------
+#
+# PySAP provides a common interface for importing and visualising
+# astronomical FITS datasets. A sample of toy datasets is provided that will be
+# used in this tutorial.
+
+import pysap
+from pprint import pprint
+from pysap.data import get_sample_data
+
+image = get_sample_data('astro-fits')
+print(image.shape, image.spacing, image.data_type)
+pprint(image.metadata)
+print(image.data.dtype)
+plt.imshow(image)
+plt.show()
+
+# %%
+# Import neuroimaging data
+# ------------------------
+#
+# PySAP also provides a common interface for importing and visualising
+# neuroimaging NIFTI datasets. A sample of toy datasets is provided that will
+# be used in this tutorial.
+
+import pysap
+from pprint import pprint
+from pysap.data import get_sample_data
+
+image = get_sample_data('mri-nifti')
+image.scroll_axis = 2
+print(image.shape, image.spacing, image.data_type)
+pprint(image.metadata)
+print(image.data.dtype)
+# image.show()  ## uncomment to visualise this object
+
+# %%
+# Decompose/recompose an image using a fast Sparse2D based transform
+# ------------------------------------------------------------------
+#
+# PySAP includes Python bindings for the Sparse2D C++ library of wavelet
+# transforms developped at the CosmoStat lab. PySAP also uses the
+# PyWavelet package. The code is optimsed and provides access to many
+# image decompsition strategies.
+#
+# All the transforms available can be listed as follows.
+
+from pprint import pprint
+import pysap
+
+pprint(pysap.wavelist())
+pprint(pysap.wavelist(family='isap-3d'))
+
+# %%
+# Here we illustrate the the decomposition/recomposition process using a
+# Daubechies (``'db3'``) wavelet from pywt with 4 scales:
+
+import pysap
+from pysap.data import get_sample_data
+
+image = get_sample_data('mri-slice-nifti')
+transform_klass = pysap.load_transform('db3')
+transform = transform_klass(nb_scale=4, verbose=1, padding_mode='symmetric')
+transform.data = image
+transform.analysis()
+# transform.show()  ## uncomment to visualise this object
+rec_image = transform.synthesis()
+# rec_image.show()  ## uncomment to visualise this object
+
+# %%
+# Here we illustrate the the decomposition/recomposition process using a
+# fast curvelet transform (``'FastCurveletTransform'``) from Sparse2D with 4
+# scales:
+
+image = get_sample_data('mri-slice-nifti')
+transform_klass = pysap.load_transform('FastCurveletTransform')
+transform = transform_klass(nb_scale=4, verbose=1, padding_mode='zero')
+transform.data = image
+transform.analysis()
+# transform.show()  ## uncomment to visualise this object
+rec_image = transform.synthesis()
+# rec_image.show()  ## uncomment to visualise this object
diff --git a/examples/structure.py b/examples/structure.py
deleted file mode 100644
index 57ab4212..00000000
--- a/examples/structure.py
+++ /dev/null
@@ -1,111 +0,0 @@
-"""
-Anstronomic/Neuroimaging common structure overview
-==================================================
-
-Credit: A Grigis
-
-pysap is a Python package related to sparsity and its application in
-astronomical or mediacal data analysis. This package also bind the 'sparse2d'
-C++ library that allows fast and extended sparse decomposition, denoising and
-deconvolution. It is accessible to everybody, and is reusable in various
-contexts. The project is hosted on github: https://github.com/CEA-COSMIC/pysap.
-
-First checks
-------------
-
-In order to test if the 'pysap' package is installed on your machine, you can
-check the package version
-"""
-
-import pysap
-
-print(pysap.__version__)
-
-#############################################################################
-# Now you can run the the configuration info function to see if all the
-# dependencies are installed properly:
-
-import pysap.configure
-
-print(pysap.configure.info())
-
-#############################################################################
-# Import astronomical data
-# ------------------------
-#
-# The package provides a common interface to import and visualize astronomical
-# FITS dataset. It also embeds a set of toy dataset that will be used during
-# this tutorial:
-
-import pysap
-from pprint import pprint
-from pysap.data import get_sample_data
-
-image = get_sample_data("astro-fits")
-print(image.shape, image.spacing, image.data_type)
-pprint(image.metadata)
-print(image.data.dtype)
-# image.show()
-
-#############################################################################
-# Import neuroimaging data
-# ------------------------
-#
-# The package provides a common interface to import and visualize neuroimaging
-# NIFTI dataset. It also embeds a set of toy dataset that will be used during
-# this tutorial:
-
-import pysap
-from pprint import pprint
-from pysap.data import get_sample_data
-
-image = get_sample_data("mri-nifti")
-image.scroll_axis = 2
-print(image.shape, image.spacing, image.data_type)
-pprint(image.metadata)
-print(image.data.dtype)
-# image.show()
-
-#############################################################################
-# Decompose/recompose an image using a fast ISAP/C++ based transform
-# ------------------------------------------------------------------
-#
-# The package provides also a common interface to the ISAP C++ software
-# developped by the COSMOSTAT lab and PyWavelet. The code is optimzed and give
-# access to many decompsition strategies. All the decompositions
-# have been declared in a registery:
-
-from pprint import pprint
-import pysap
-
-pprint(pysap.wavelist())
-pprint(pysap.wavelist(family="isap-3d"))
-
-#############################################################################
-# We illustrate the the decompose/recompose using a 'Daubechies' from pywt
-# and 4 scales:
-
-import pysap
-from pysap.data import get_sample_data
-
-image = get_sample_data("mri-slice-nifti")
-transform_klass = pysap.load_transform("db3")
-transform = transform_klass(nb_scale=4, verbose=1, padding_mode="symmetric")
-transform.data = image
-transform.analysis()
-# transform.show()
-rec_image = transform.synthesis()
-# rec_image.show()
-
-#############################################################################
-# We illustrate the the decompose/recompose using a 'FastCurveletTransform'
-# from ISAP and 4 scales:
-
-image = get_sample_data("mri-slice-nifti")
-transform_klass = pysap.load_transform("FastCurveletTransform")
-transform = transform_klass(nb_scale=4, verbose=1, padding_mode="zero")
-transform.data = image
-transform.analysis()
-# transform.show()
-rec_image = transform.synthesis()
-# rec_image.show()
diff --git a/plugins.txt b/plugins.txt
new file mode 100644
index 00000000..ee8385ab
--- /dev/null
+++ b/plugins.txt
@@ -0,0 +1,3 @@
+pysap-astro==v0.0.2
+pysap-etomo==v0.0.1
+pysap-mri==v0.4.0
diff --git a/pysap/__init__.py b/pysap/__init__.py
index abf63fbe..2cbc1862 100755
--- a/pysap/__init__.py
+++ b/pysap/__init__.py
@@ -1,16 +1,18 @@
 # -*- coding: utf-8 -*-
 ##########################################################################
-# pySAP - Copyright (C) CEA, 2017 - 2018
+# PySAP - Copyright (C) CEA, 2017 - 2018
 # Distributed under the terms of the CeCILL-B license, as published by
 # the CEA-CNRS-INRIA. Refer to the LICENSE file or to
 # http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
 # for details.
 ##########################################################################
 
-"""
-pySap is a Python package related to sparsity and its application in
-astronomical data analysis. This package is based on the 'sparse2d' C++ library
-that allows sparse decomposition, denoising and deconvolution.
+"""PySAP.
+
+This section provides full API documentation for the core PySAP package, which
+handles :py:class:`Image <pysap.base.image.Image>` objects, data
+:py:class:`transforms <pysap.extensions.transform>` and other key features.
+
 """
 
 # import matplotlib
diff --git a/pysap/base/image.py b/pysap/base/image.py
index 9147eb30..c3b699f4 100644
--- a/pysap/base/image.py
+++ b/pysap/base/image.py
@@ -9,6 +9,7 @@
 
 # System import
 import numpy
+from warnings import warn
 
 # Package import
 from .observable import Observable
@@ -88,6 +89,12 @@ def __init__(self, shape=None, spacing=None, data_type="scalar",
     def show(self):
         """ Display the image data.
         """
+        warn(
+            'The show method has been deprecated and will be removed in a '
+            + 'future release. In the future Please use the Image.data '
+            + 'attribute and your plotting package of choice.',
+            FutureWarning
+        )
         plot_data(self.data, scroll_axis=self._scroll_axis)
 
     def modified(self):
diff --git a/pysap/base/io.py b/pysap/base/io.py
index eaf73b44..53d51b03 100644
--- a/pysap/base/io.py
+++ b/pysap/base/io.py
@@ -17,10 +17,11 @@
 from pysap.base.loaders import NIFTI
 from pysap.base.loaders import npBinary
 from pysap.base.loaders import MAT
+from pysap.base.loaders import TIF
 
 # Global parameters
 # > define all the available loaders
-LOADERS = [FITS, NIFTI, npBinary, MAT]
+LOADERS = [FITS, NIFTI, npBinary, MAT, TIF]
 
 
 def load(path, dtype=numpy.single, **kwargs):
@@ -54,7 +55,7 @@ def save(image, path, **kwargs):
 
     Parameters
     ----------
-    image: Image or ndarray
+    image: Image or numpy.ndarray
         the data to be saved.
     path: str
         the destination file.
diff --git a/pysap/base/loaders/__init__.py b/pysap/base/loaders/__init__.py
index 0e555c44..b19bc60c 100755
--- a/pysap/base/loaders/__init__.py
+++ b/pysap/base/loaders/__init__.py
@@ -16,3 +16,4 @@
 from .fits import FITS
 from .numpy_binary import npBinary
 from .mat import MAT
+from .tif import TIF
diff --git a/pysap/base/loaders/mat.py b/pysap/base/loaders/mat.py
index af1bf084..9e8662e0 100644
--- a/pysap/base/loaders/mat.py
+++ b/pysap/base/loaders/mat.py
@@ -30,7 +30,7 @@ def load(self, path, image_field="image", meta_field="metadata"):
             the path to the data to be loaded.
         image_field: str, default 'image'
             the name of the data field that contains the image array.
-        image_field: str, default 'metadata'
+        meta_field: str, default 'metadata'
             the name of the data field that contains the image metadata.
 
         Returns
@@ -58,7 +58,7 @@ def save(self, image, outpath, image_field="image", meta_field="metadata"):
             the path where the the image will be saved.
         image_field: str, default 'image'
             the name of the data field that contains the image array.
-        image_field: str, default 'metadata'
+        meta_field: str, default 'metadata'
             the name of the data field that contains the image metadata.
         """
         data = {
diff --git a/pysap/base/loaders/nifti.py b/pysap/base/loaders/nifti.py
index 1c203be0..e331dc0e 100644
--- a/pysap/base/loaders/nifti.py
+++ b/pysap/base/loaders/nifti.py
@@ -38,7 +38,7 @@ def load(self, path):
         return Image(spacing=_image.header.get_zooms(),
                      data_type="scalar",
                      metadata={"path": path},
-                     data=_image.get_data())
+                     data=_image.get_fdata())
 
     def save(self, image, outpath):
         """ A method that save the image data and associated metadata.
diff --git a/pysap/base/loaders/tif.py b/pysap/base/loaders/tif.py
new file mode 100644
index 00000000..0d461cbc
--- /dev/null
+++ b/pysap/base/loaders/tif.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+##########################################################################
+# pySAP - Copyright (C) CEA, 2017 - 2018
+# Distributed under the terms of the CeCILL-B license, as published by
+# the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+# http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+# for details.
+##########################################################################
+
+# Package import
+from .loader_base import LoaderBase
+from pysap.base.image import Image
+
+# Third party import
+from skimage.io import imread, imsave
+
+
+class TIF(LoaderBase):
+    """Define the '.tif' file loader.
+    """
+    allowed_extensions = [".tif"]
+
+    def load(self, path, as_gray=False):
+        """ A method that load the data in TIF format.
+
+        Parameters
+        ----------
+        path: str
+            the path to the data to be loaded.
+        as_gray: bool, default False (display image in RGB by default)
+
+        Returns
+        -------
+        image: Image
+            the loaded image.
+        """
+        _array = imread(path, as_gray)
+        _meta = {"path": path}
+        return Image(data_type="scalar",
+                     metadata=_meta,
+                     data=_array)
+
+    def save(self, image, outpath, check_contrast=True):
+        """A method that save the image in TIF format.
+
+        Parameters
+        ----------
+        image: Image
+            the image to be saved.
+        outpath: str
+            the path where the the image will be saved.
+        check_contrast: bool, optional
+            Check for low contrast and print warning (default: True).
+        """
+        imsave(outpath, image.data, check_contrast)
diff --git a/pysap/base/observable.py b/pysap/base/observable.py
index d6783d05..4d07ed20 100755
--- a/pysap/base/observable.py
+++ b/pysap/base/observable.py
@@ -49,7 +49,7 @@ def add_observer(self, signal, observer):
         ----------
         signal: str
             a valid signal.
-        observer: @func
+        observer: callable
             a function that will be called when the signal is emitted.
         """
         self._is_allowed_signal(signal)
@@ -63,7 +63,7 @@ def remove_observer(self, signal, observer):
         ----------
         signal: str
             a valid signal.
-        observer: @func
+        observer: callable
             an obervation function to be removed.
         """
         self._is_allowed_event(signal)
@@ -140,7 +140,7 @@ def _add_observer(self, signal, observer):
         ----------
         signal: str
             a valid signal.
-        observer: @func
+        observer: callable
             an obervation function.
         """
         if observer not in self._observers[signal]:
@@ -153,7 +153,7 @@ def _remove_observer(self, signal, observer):
         ----------
         signal: str
             a valid signal.
-        observer: @func
+        observer: callable
             an obervation function to be removed.
         """
         if observer in self._observers[signal]:
diff --git a/pysap/base/plugins.py b/pysap/base/plugins.py
index f40de8d1..7daed8f0 100644
--- a/pysap/base/plugins.py
+++ b/pysap/base/plugins.py
@@ -33,7 +33,7 @@ def find_module(self, name, path=None):
         package/module-finding code kicks in.
         """
         # Use this loader only on registered modules
-        match = re.match("pysap\.plugins\.(.*)", name)
+        match = re.match(r"pysap\.plugins\.(.*)", name)
         if match is None:
             return None
         name = match.groups()[0]
diff --git a/pysap/base/transform.py b/pysap/base/transform.py
index 4a5bc0fe..ae1e7830 100644
--- a/pysap/base/transform.py
+++ b/pysap/base/transform.py
@@ -15,7 +15,7 @@
 from pprint import pprint
 import uuid
 import os
-import warnings
+from warnings import warn
 
 # Package import
 import pysap
@@ -64,7 +64,7 @@ def __init__(self, nb_scale, verbose=0, dim=2, use_wrapping=False,
 
         Parameters
         ----------
-        data: ndarray
+        data: numpy.ndarray
             the input data.
         nb_scale: int
             the number of scale of the decomposition that includes the
@@ -123,7 +123,7 @@ def __getitem__(self, given):
 
         Returns
         -------
-        coeffs: ndarray or list of ndarray
+        coeffs: numpy.ndarray or list
             the analysis coefficients.
         """
         # Convert given index to generic scale/band index
@@ -168,7 +168,7 @@ def __setitem__(self, given, array):
         ----------
         given: tuple
             the scale and band indices.
-        array: ndarray
+        array: numpy.ndarray
             the specific scale/band data as an array.
         """
         # Check that we have a valid given index
@@ -215,7 +215,7 @@ def _set_data(self, data):
 
         Parameters
         ----------
-        data: nd-array or pysap.Image
+        data: numpy.ndarray or pysap.Image
             input data/signal.
         """
         if self.verbose > 0 and self._data is not None:
@@ -247,7 +247,7 @@ def _get_data(self):
 
         Returns
         -------
-        data: nd-array
+        data: numpy.ndarray
             input data/signal.
         """
         return self._data
@@ -257,7 +257,7 @@ def _set_analysis_data(self, analysis_data):
 
         Parameters
         ----------
-        analysis_data: lsit of nd-array
+        analysis_data: list
             decomposition coefficients array.
         """
         if self.verbose > 0 and self._analysis_data is not None:
@@ -273,7 +273,7 @@ def _get_analysis_data(self):
 
         Returns
         -------
-        analysis_data: nd-array
+        analysis_data: numpy.ndarray
             decomposition coefficients array.
         """
         return self._analysis_data
@@ -323,10 +323,10 @@ def bands_shapes(cls, bands_lengths, ratio=None):
 
         Parameters
         ----------
-        bands_lengths: ndarray (<nb_scale>, max(<nb_band_per_scale>, 0))
+        bands_lengths: numpy.ndarray (<nb_scale>, max(<nb_band_per_scale>, 0))
             array holding the length between two bands of the data
             vector per scale.
-        ratio: ndarray, default None
+        ratio: numpy.ndarray, default None
             a array containing ratios for eeach scale and each band.
 
         Returns
@@ -352,6 +352,14 @@ def bands_shapes(cls, bands_lengths, ratio=None):
     def show(self):
         """ Display the different bands at the different decomposition scales.
         """
+        warn(
+            'The show method has been deprecated and will be removed in a '
+            + 'future release. In the future Please use the Transform.data '
+            + 'and/or Transform.analysis_data attributes and your plotting '
+            + 'package of choice. To use this deprecated function you will '
+            + 'need to install pyqtgraph manually.',
+            FutureWarning
+        )
         plot_transform(self)
 
     def analysis(self, **kwargs):
@@ -490,7 +498,7 @@ def _get_linear_band(self, scale, band, analysis_data):
             index of the scale.
         band: int
             index of the band.
-        analysis_data: nd-array (N, )
+        analysis_data: numpy.ndarray (N, )
             the analysis buffer.
 
         Returns
@@ -521,9 +529,9 @@ def _set_linear_band(self, scale, band, analysis_data, band_data):
             index of the scale.
         band: int
             index of the band.
-        analysis_data: nd-array (N, )
+        analysis_data: numpy.ndarray (N, )
             the analysis buffer.
-        band_data: nd-array (M, M)
+        band_data: numpy.ndarray (M, M)
             the band data to be added in the analysis buffer.
 
         Returns
@@ -563,9 +571,9 @@ def _set_transformation_parameters(self):
         is_decimated: bool
             True if the decomposition include a decimation of the
             band number of coefficients.
-        nb_band_per_scale: ndarray (<nb_scale>, )
+        nb_band_per_scale: numpy.ndarray (<nb_scale>, )
             vector of int holding the number of band per scale.
-        bands_lengths: ndarray (<nb_scale>, max(<nb_band_per_scale>, 0))
+        bands_lengths: numpy.ndarray (<nb_scale>, max(<nb_band_per_scale>, 0))
             array holding the length between two bands of the data
             vector per scale.
         bands_shapes: list of list of 2-uplet (<nb_scale>, <nb_band_per_scale>)
@@ -581,9 +589,9 @@ def _compute_transformation_parameters(self):
 
         Attributes
         ----------
-        scales_lengths: ndarray (<nb_scale>, )
+        scales_lengths: numpy.ndarray (<nb_scale>, )
             the length of each band.
-        scales_padds: ndarray (<nb_scale> + 1, )
+        scales_padds: numpy.ndarray (<nb_scale> + 1, )
             the index of the data associated to each scale.
         """
         if self.bands_lengths is None:
@@ -598,7 +606,7 @@ def _analysis(self, data, **kwargs):
 
         Parameters
         ----------
-        data: nd-array
+        data: numpy.ndarray
             a real array to be decomposed.
         kwargs: dict (optional)
             the parameters that will be passed to
@@ -606,7 +614,7 @@ def _analysis(self, data, **kwargs):
 
         Returns
         -------
-        analysis_data: nd_array
+        analysis_data: numpy.ndarray
             the decomposition coefficients.
         analysis_header: dict
             the decomposition associated information.
@@ -619,14 +627,14 @@ def _synthesis(self, analysis_data, analysis_header):
 
         Parameters
         ----------
-        analysis_data: list of nd-array
+        analysis_data: list of numpy.ndarray
             the wavelet coefficients array.
         analysis_header: dict
             the wavelet decomposition parameters.
 
         Returns
         -------
-        data: nd-array
+        data: numpy.ndarray
             the reconstructed data array.
         """
         raise NotImplementedError("Abstract method should not be declared "
diff --git a/pysap/base/utils.py b/pysap/base/utils.py
index 59416f95..f40471aa 100644
--- a/pysap/base/utils.py
+++ b/pysap/base/utils.py
@@ -100,12 +100,12 @@ def flatten(x):
 
     Parameters
     ----------
-    x: list of ndarray or ndarray
+    x: list or numpy.ndarray
         the input dataset.
 
     Returns
     -------
-    y: ndarray 1D
+    y: numpy.ndarray 1D
         the flatten input list of array.
     shape: list of uplet
         the input list of array structure.
@@ -131,14 +131,14 @@ def unflatten(y, shape):
 
     Parameters
     ----------
-    y: ndarray 1D
+    y: numpy.ndarray 1D
         a flattened input array.
     shape: list of uplet
         the output structure information.
 
     Returns
     -------
-    x: list of ndarray
+    x: list
         the unflattened dataset.
     """
     # Unflatten the dataset
diff --git a/pysap/configure.py b/pysap/configure.py
index 1aa99fed..d2cb4c06 100644
--- a/pysap/configure.py
+++ b/pysap/configure.py
@@ -31,7 +31,7 @@ def _check_python_versions():
 
     Returns
     -------
-    versions: dict with 2-uplet
+    dict
         the minimum required version and the installed version for each module.
         '?' means no package found.
     """
@@ -50,7 +50,7 @@ def _check_python_versions():
             mod_name = "pywt"
         try:
             mod_install_version = importlib.import_module(mod_name).__version__
-        except:
+        except ImportError:
             mod_install_version = "?"
         versions[mod_name] = (operator + mod_min_version, mod_install_version)
     return versions
diff --git a/pysap/data.py b/pysap/data.py
index 19641090..053ffe4a 100644
--- a/pysap/data.py
+++ b/pysap/data.py
@@ -32,7 +32,7 @@
 
 
 # Global parameters
-SAMPLE_DATE_FILES = {
+SAMPLE_DATA_FILES = {
     "dict-learn-dataset": {
         "url": ("ftp://ftp.cea.fr/pub/unati/nsap/pysap/datasets/"
                 "training_database.npy"),
@@ -42,7 +42,7 @@
         "url": ("ftp://ftp.cea.fr/pub/unati/nsap/pysap/datasets/"
                 "orange_phantom_3d_pmri_images.npy"),
         "md5sum": "e4ac268fde0226c6fdcf2e9b62b240f0",
-        "dtype": numpy.complex
+        "dtype": numpy.complex_
     },
     "2d-pmri": {
         "url": ("https://github.com/CEA-COSMIC/pysap-data/raw/"
@@ -118,6 +118,11 @@
         "url": ("https://github.com/CEA-COSMIC/pysap-data/raw/"
                 "master/pysap-data/gen.mr"),
         "md5sum": None
+    },
+    "eels-gst-2d-etomo": {
+        "url": ("https://github.com/CEA-COSMIC/pysap-data/raw/"
+                "master/pysap-data/EELS_GST_4_5_ali.tif"),
+        "md5sum": None
     }
 }
 DATADIR = os.path.join(os.path.expanduser("~"), ".local", "share", "pysap")
@@ -133,7 +138,7 @@ def get_sample_data(dataset_name, datadir=DATADIR, verbose=1):
     Parameters
     ----------
     dataset_name: str
-        which sample data you want, must be defined in the 'SAMPLE_DATE_FILES'
+        which sample data you want, must be defined in the 'SAMPLE_DATA_FILES'
         dictionary.
     verbose: int (optional, default 1)
         control the verbosity level.
@@ -144,11 +149,11 @@ def get_sample_data(dataset_name, datadir=DATADIR, verbose=1):
         the loaded dataset.
     """
     # First get the data url
-    dataset = copy.deepcopy(SAMPLE_DATE_FILES.get(dataset_name))
+    dataset = copy.deepcopy(SAMPLE_DATA_FILES.get(dataset_name))
     if dataset is None:
         raise Exception("No '{0}' sample data available - allowed sample data "
                         "are {1}.".format(dataset_name,
-                                          SAMPLE_DATE_FILES.keys()))
+                                          SAMPLE_DATA_FILES.keys()))
 
     # Get the resource on the web or on the local machine
     dataset["url"] = dataset["url"].format(**{"PYSAP": PACKAGEDIR})
@@ -252,11 +257,14 @@ def download_file(url, data_dir, resume=True, overwrite=False, verbose=0):
 
     Returns
     -------
-    download_fname: str
+    str
         absolute path to the downloaded file.
 
-    Note: If, for any reason, the download procedure fails, all downloaded
+    Notes
+    -----
+    If, for any reason, the download procedure fails, all downloaded
     files are removed.
+
     """
     # Create the download directory if necessary
     if not os.path.exists(data_dir):
@@ -292,9 +300,10 @@ def download_file(url, data_dir, resume=True, overwrite=False, verbose=0):
     # Test if the dataset has been released
     try:
         urlopen(url)
-    except:
+    except Exception as exc:
         raise ValueError(
-            "The '{0}' dataset has not been released yet.".format(url))
+            "The '{0}' dataset has not been released yet.".format(url)
+        ) from exc
 
     # Start downloading dataset
     local_file = None
diff --git a/pysap/extensions/formating.py b/pysap/extensions/formating.py
index 21e63302..818ca2d5 100644
--- a/pysap/extensions/formating.py
+++ b/pysap/extensions/formating.py
@@ -21,7 +21,7 @@
 # GETTERS
 
 def get_hbl(A):
-    """ Return the half-bottom-left of the given array.
+    """Return the half-bottom-left of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -29,7 +29,7 @@ def get_hbl(A):
 
 
 def get_hbr(A):
-    """ Return the half-bottom-right of the given array.
+    """Return the half-bottom-right of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -37,7 +37,7 @@ def get_hbr(A):
 
 
 def get_htl(A):
-    """ Return the half-top-left of the given array.
+    """Return the half-top-left of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -45,7 +45,7 @@ def get_htl(A):
 
 
 def get_htr(A):
-    """ Return the half-top-right of the given array.
+    """Return the half-top-right of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -53,7 +53,7 @@ def get_htr(A):
 
 
 def get_hr(A):
-    """ Return the half-right of the given array.
+    """Return the half-right of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -61,7 +61,7 @@ def get_hr(A):
 
 
 def get_hl(A):
-    """ Return the half-left of the given array.
+    """Return the half-left of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -69,7 +69,7 @@ def get_hl(A):
 
 
 def get_hb(A):
-    """ Return the half-bottom of the given array.
+    """Return the half-bottom of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -77,7 +77,7 @@ def get_hb(A):
 
 
 def get_ht(A):
-    """ Return the half-top of the given array.
+    """Return the half-top of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -87,7 +87,7 @@ def get_ht(A):
 
 
 def set_hbl(A, a):
-    """ Return the half-bottom-left of the given array.
+    """Return the half-bottom-left of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -95,7 +95,7 @@ def set_hbl(A, a):
 
 
 def set_hbr(A, a):
-    """ Return the half-bottom-right of the given array.
+    """Return the half-bottom-right of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -103,7 +103,7 @@ def set_hbr(A, a):
 
 
 def set_htl(A, a):
-    """ Return the half-top-left of the given array.
+    """Return the half-top-left of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -111,7 +111,7 @@ def set_htl(A, a):
 
 
 def set_htr(A, a):
-    """ Return the half-top-right of the given array.
+    """Return the half-top-right of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -119,7 +119,7 @@ def set_htr(A, a):
 
 
 def set_hr(A, a):
-    """ Return the half-right of the given array.
+    """Return the half-right of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -127,7 +127,7 @@ def set_hr(A, a):
 
 
 def set_hl(A, a):
-    """ Return the half-left of the given array.
+    """Return the half-left of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -135,7 +135,7 @@ def set_hl(A, a):
 
 
 def set_hb(A, a):
-    """ Return the half-bottom of the given array.
+    """Return the half-bottom of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -143,7 +143,7 @@ def set_hb(A, a):
 
 
 def set_ht(A, a):
-    """ Return the half-top of the given array.
+    """Return the half-top of the given array.
     """
     nx, _ = A.shape
     li = int(nx / 2)
@@ -154,42 +154,44 @@ def set_ht(A, a):
 
 
 def flatten_undecimated_n_bands(cube, trf):
-    """ Flatten the decomposition coefficients from a 'cube' to a vector.
-    'flatten_undecimated_n_bands' concern the 'cube' where each layer
-    correspond to a
-    undecimated band. We can have multiple bands per scale, which lead to
-    nb_scale * nb_band_per_scale for one dimension.
+    """Flatten the decomposition coefficients from a cube to a vector.
+    ``flatten_undecimated_n_bands`` concerns the cube where each layer
+    correspond to a undecimated band. We can have multiple bands per scale,
+    which lead to ``nb_scale * nb_band_per_scale`` for one dimension.
 
     Parameters
     ----------
-    cube: np.ndarray, the cube that containes the decomposition
-    coefficients.
+    cube: numpy.ndarray
+        The cube that contains the decomposition coefficients
 
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The flattened cube
+
     """
     return np.copy(cube.flatten())
 
 
 def flatten_decimated_1_bands(cube, trf):
-    """ Flatten the decomposition coefficients from a 'cube' to a vector.
-    'flatten_decimated_1_bands' concern the 'cube' where it's actually a
-    2d-array like
-    the classic wavelet 2d-transform of 1 bands. It has the same formating
-    than the 3 bands but the 'v' and 'h' bands or set to 0.
+    """Flatten the decomposition coefficients from a cube to a vector.
+    ``flatten_decimated_1_bands`` concerns the cube where it's actually a
+    2d-array like the classic wavelet 2d-transform of 1 bands. It has the same
+    formating than the 3 bands but the ``v`` and ``h`` bands or set to ``0``.
 
     Parameters
     ----------
-    cube: np.ndarray, the cube that containes the decomposition
-    coefficients.
+    cube: numpy.ndarray
+        The cube that contains the decomposition coefficients
 
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The flattened cube
+
     """
     pieces = []
-    for i in range(trf.nb_scale-1):
+    for i in range(trf.nb_scale - 1):
         pieces.append(get_htl(cube).flatten())
         cube = get_hbr(cube)
     pieces.append(cube.flatten())  # get approx
@@ -197,22 +199,23 @@ def flatten_decimated_1_bands(cube, trf):
 
 
 def flatten_decimated_3_bands(cube, trf):
-    """ Flatten the decomposition coefficients from a 'cube' to a vector.
-    'flatten_decimated_3_bands' concern the 'cube' where it's actually
-    a 2d-array like
-    the classic wavelet 2d-transform of 3 bands.
+    """Flatten the decomposition coefficients from a cube to a vector.
+    ``flatten_decimated_3_bands`` concerns the cube where it's actually
+    a 2d-array like the classic wavelet 2d-transform of 3 bands.
 
     Parameters
     ----------
-    cube: np.ndarray, the cube that containes the decomposition
-    coefficients.
+    cube: numpy.ndarray
+        The cube that containes the decomposition coefficients
 
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The flattened cube
+
     """
     pieces = []
-    for i in range(trf.nb_scale-1):
+    for i in range(trf.nb_scale - 1):
         pieces.append(get_htr(cube).flatten())
         pieces.append(get_hbr(cube).flatten())
         pieces.append(get_hbl(cube).flatten())
@@ -222,18 +225,20 @@ def flatten_decimated_3_bands(cube, trf):
 
 
 def flatten_vector(cube, trf):
-    """ Flatten the decomposition coefficients from a 'cube' to a vector.
-    'flatten_vector' concern the 'curvelet-cube' where it's already
+    """Flatten the decomposition coefficients from a cube to a vector.
+    ``flatten_vector`` concerns the ``curvelet-cube`` where it's already
     a vector.
 
     Parameters
     ----------
-    cube: np.ndarray, the cube that containes the decomposition
-    coefficients.
+    cube: numpy.ndarray
+        The cube that containes the decomposition coefficients
 
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The flattened cube
+
     """
     metadata_len = 1 + trf.nb_scale + 2 * trf.nb_band_per_scale.sum()
     data = np.zeros(len(cube) - metadata_len)
@@ -241,8 +246,8 @@ def flatten_vector(cube, trf):
     data_padd = 0
     for ks in range(trf.nb_scale):
         for kb in range(trf.nb_band_per_scale[ks]):
-            tmp = cube[cube_padd:cube_padd+trf.bands_lengths[ks, kb]]
-            data[data_padd:data_padd+trf.bands_lengths[ks, kb]] = tmp
+            tmp = cube[cube_padd:cube_padd + trf.bands_lengths[ks, kb]]
+            data[data_padd:data_padd + trf.bands_lengths[ks, kb]] = tmp
             Nx = trf.bands_shapes[ks][kb][0]
             Ny = trf.bands_shapes[ks][kb][1]
             cube_padd += (Nx * Ny + 2)
@@ -251,21 +256,23 @@ def flatten_vector(cube, trf):
 
 
 def flatten_decimated_feauveau(cube, trf):
-    """ Flatten decomposition coefficients from a 'cube' to a vector.
-    'flatten_decimated_feauveau' concern the 'cube' where it's the Feauveau
-    decimated...
+    """Flatten decomposition coefficients from a cube to a vector.
+    ``flatten_decimated_feauveau`` concern the cube where it's the Feauveau
+    decimated.
 
     Parameters
     ----------
-    cube: np.ndarray, the cube that containes the decomposition
-    coefficients.
+    cube: numpy.ndarray
+        The cube that containes the decomposition coefficients
 
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The flattened cube
+
     """
     pieces = []
-    for i in range(trf.nb_scale-1):
+    for i in range(trf.nb_scale - 1):
         pieces.append(get_hbl(cube).flatten())
         pieces.append(get_hr(cube).flatten())
         cube = get_htl(cube)
@@ -278,95 +285,84 @@ def flatten_decimated_feauveau(cube, trf):
 
 
 def inflated_undecimated_n_bands(trf):
-    """ Inflated the decomposition coefficients from a vector to a 'cube'.
-    'inflated_undecimated_n_bands' concern the vector where each layer
+    """Inflated the decomposition coefficients from a vector to a cube.
+    ``inflated_undecimated_n_bands`` concerns the vector where each layer
     correspond to a undecimated band. We can have multiple bands per scale,
-    which lead to nb_scale * nb_band_per_scale for one dimension.
-
-    Parameters
-    ----------
-    vector: np.ndarray, the vector that containes the decomposition
-    coefficients.
+    which lead to ``nb_scale * nb_band_per_scale`` for one dimension.
 
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The unflattened cube
+
     """
     return np.copy(trf._analysis_data.reshape(trf._analysis_shape))
 
 
 def inflated_decimated_1_bands(trf):
-    """ Inflated the decomposition coefficients from a vector to a 'cube'.
-    'inflated_decimated_1_bands' concern the vector where it's actually a
+    """Inflated the decomposition coefficients from a vector to a cube.
+    ``inflated_decimated_1_bands`` concerns the vector where it's actually a
     2d-array like the classic wavelet 2d-transform of 1 bands. It has the
-    same formating than the 3 bands but the 'v' and 'h' bands or set to 0.
-
-    Parameters
-    ----------
-    vector: np.ndarray, the vector that containes the decomposition
-    coefficients.
+    same formating than the 3 bands but the ``v`` and ``h`` bands or set to
+    ``0``.
 
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The unflattened cube
+
     """
     cube = np.zeros(trf._analysis_shape, dtype=trf._analysis_data.dtype)
     tmp = cube
-    for ks in range(trf.nb_scale-1):
+    for ks in range(trf.nb_scale - 1):
         set_htl(tmp, trf[ks, 0])
-        if ks == ((trf.nb_scale-1)-1):
+        if ks == ((trf.nb_scale - 1) - 1):
             break
         else:
             tmp = get_hbr(tmp)
-    set_hbr(tmp, trf[trf.nb_scale-1, 0])  # set approx
+    set_hbr(tmp, trf[trf.nb_scale - 1, 0])  # set approx
     return cube
 
 
 def inflated_decimated_3_bands(trf):
-    """ Inflated the decomposition coefficients from a vector to a 'cube'.
-    'inflated_decimated_3_bands' concern the vector where it's actually a
+    """Inflated the decomposition coefficients from a vector to a cube.
+    ``inflated_decimated_3_bands`` concern the vector where it's actually a
     2d-array like the classic wavelet 2d-transform of 3 bands.
 
-    Parameters
-    ----------
-    vector: np.ndarray, the vector that containes the decomposition
-    coefficients.
-
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The unflattened cube
+
     """
     cube = np.zeros(trf._analysis_shape, dtype=trf._analysis_data.dtype)
     tmp = cube
-    for ks in range(trf.nb_scale-1):
+    for ks in range(trf.nb_scale - 1):
         set_htr(tmp, trf[ks, 0])
         set_hbr(tmp, trf[ks, 1])
         set_hbl(tmp, trf[ks, 2])
-        if ks == ((trf.nb_scale-1)-1):
+        if ks == ((trf.nb_scale - 1) - 1):
             break
         else:
             tmp = get_htl(tmp)
-    set_htl(tmp, trf[trf.nb_scale-1, 0])  # set approx
+    set_htl(tmp, trf[trf.nb_scale - 1, 0])  # set approx
     return cube
 
 
 def inflated_vector(trf):
-    """ Inflated the decomposition coefficients from a vector to a 'cube'.
-    'inflated_vector' concern the vector where it's encode a curvelet
-
-    Parameters
-    ----------
-    vector np.ndarray, the vector that containes the decomposition
-    coefficients.
+    """Inflated the decomposition coefficients from a vector to a cube.
+    ``inflated_vector`` concerns the vector where it's encode a curvelet.
 
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The unflattened cube
+
     """
     metadata_len = 1 + trf.nb_scale + 2 * trf.nb_band_per_scale.sum()
     cube = np.zeros(len(trf._analysis_data) + metadata_len)
     cube[0] = trf.nb_scale
-    cube[1:1+trf.nb_scale] = trf.nb_band_per_scale
+    cube[1:1 + trf.nb_scale] = trf.nb_band_per_scale
     cube_padd = 1 + trf.nb_scale
     data_padd = 0
     for ks in range(trf.nb_scale):
@@ -378,36 +374,33 @@ def inflated_vector(trf):
             cube[cube_padd] = Ny
             cube_padd += 1
             tmp = trf[ks, kb].flatten()
-            cube[cube_padd:cube_padd+trf.bands_lengths[ks, kb]] = tmp
+            cube[cube_padd:cube_padd + trf.bands_lengths[ks, kb]] = tmp
             cube_padd += (Nx * Ny)
             data_padd += (Nx * Ny)
     return cube
 
 
 def inflated_decimated_feauveau(trf):
-    """ Inflated the decomposition coefficients from a vector to a 'cube'.
-    'inflated_decimated_feauveau' concern the vector where it's the
-    Feauveau decimated...
-
-    Parameters
-    ----------
-    vector: np.ndarray, the vector that containes the decomposition
-    coefficients.
+    """Inflated the decomposition coefficients from a vector to a cube.
+    ``inflated_decimated_feauveau`` concerns the vector where it's the
+    Feauveau decimated.
 
     Returns
     -------
-    data: np.ndarray, the flatten 'cube'.
+    numpy.ndarray
+        The unflattened cube
+
     """
     cube = np.zeros(trf._analysis_shape, dtype=trf._analysis_data.dtype)
     tmp = cube
-    for ks in range(trf.nb_scale-1):
+    for ks in range(trf.nb_scale - 1):
         set_hbl(tmp, trf[ks, 0])
         set_hr(tmp, trf[ks, 1])
-        if ks == ((trf.nb_scale-1)-1):
+        if ks == ((trf.nb_scale - 1) - 1):
             break
         else:
             tmp = get_htl(tmp)
-    set_htl(tmp, trf[trf.nb_scale-1, 0])  # set approx
+    set_htl(tmp, trf[trf.nb_scale - 1, 0])  # set approx
     return cube
 
 ###
diff --git a/pysap/extensions/sparse2d.py b/pysap/extensions/sparse2d.py
index aedb724e..bc3ee2f8 100644
--- a/pysap/extensions/sparse2d.py
+++ b/pysap/extensions/sparse2d.py
@@ -50,7 +50,7 @@ def __init__(self, **kwargs):
         iter_max: double
         epsilon: float
         verbose: Boolean
-        tab_n_sigma: ndarray
+        tab_n_sigma: numpy.ndarray
         suppress_isolated_pixels: Boolean
 
         """
@@ -62,7 +62,7 @@ def filter(self, data):
 
         Parameters
         ----------
-        data: ndarray
+        data: numpy.ndarray
             the input data.
         """
         self.data = pysap.Image(data=self.flt.filter(data))
@@ -120,9 +120,9 @@ def deconvolve(self, img, psf):
 
         Parameters
         ----------
-        img: ndarray
+        img: numpy.ndarray
             the input image.
-        psf: ndarray
+        psf: numpy.ndarray
             the input psf
         """
         self.data = pysap.Image(data=self.deconv.deconvolve(img, psf))
@@ -162,7 +162,7 @@ def transform(self, data):
 
         Parameters
         ----------
-        data: ndarray
+        data: numpy.ndarray
             the input data.
         """
         self.cube = self.trf.transform(data)
@@ -172,7 +172,7 @@ def reconstruct(self, data):
 
         Parameters
         ----------
-        data: ndarray
+        data: numpy.ndarray
             the input data.
         """
         self.recons = self.trf.reconstruct(data)
diff --git a/pysap/extensions/tools.py b/pysap/extensions/tools.py
index 62b2f3a4..893802a7 100644
--- a/pysap/extensions/tools.py
+++ b/pysap/extensions/tools.py
@@ -18,7 +18,7 @@ def mr_transform(
         nbiter=3, type_of_filters=1, use_l2_norm=False,
         type_of_non_orthog_filters=2, number_of_undecimated_scales=None,
         verbose=False):
-    """ Wrap the Sparse2d 'mr_transform'.
+    """ Wrap the Sparse2d ``mr_transform``.
     """
     # Generate the command
     cmd = [
@@ -56,7 +56,7 @@ def mr_filter(
         sigma=None, type_of_noise=1, number_of_scales=4,
         number_of_iterations=10, epsilon=0.001, verbose=False,
         tab_n_sigma=[], suppress_isolated_pixels=False):
-    """ Wrap the Sparse2d 'mr_filter'.
+    """ Wrap the Sparse2d ``mr_filter``.
     """
     # Generate the command
     cmd = ["mr_filter"]
@@ -101,7 +101,7 @@ def mr_deconv(
         no_auto_shift_max_psf=False, detect_only_positive_structure=False,
         suppress_isolated_pixels=False, suppress_last_scale=False,
         optimization=None, regul_param=0, verbose=False):
-    """ Wrap the Sparse2d 'mr_deconv'.
+    """ Wrap the Sparse2d ``mr_deconv``.
     """
     # Generate the command
     cmd = ["mr_deconv"]
@@ -156,7 +156,7 @@ def mr_deconv(
 
 def mr_recons(
         in_mr_file, out_image, verbose=False):
-    """ Wrap the Sparse2d 'mr_recons'.
+    """ Wrap the Sparse2d ``mr_recons``.
     """
     # Generate the command
     cmd = ["mr_recons"]
@@ -170,7 +170,7 @@ def mr_recons(
 
 
 def mr3d_recons(in_mr_file, out_image, verbose=False):
-    """ Wrap the Sparse2d 'mr3d_recons'.
+    """ Wrap the Sparse2d ``mr3d_recons``.
     """
     # Generate the command
     cmd = ["mr3d_recons"]
@@ -188,7 +188,7 @@ def mr3d_transform(
         type_of_lifting_transform=3, number_of_scales=4,
         type_of_filters=1, use_l2_norm=False,
         verbose=False):
-    """ Wrap the Sparse2d 'mr3d_trans'.
+    """ Wrap the Sparse2d ``mr3d_trans``.
     """
     # Generate the command
     cmd = [
@@ -242,7 +242,7 @@ def mr3d_filter(
         sigma=None, correlated_noise=None, number_of_scales=4,
         nsigma=3,
         verbose=False):
-    """ Wrap the Sparse2d 'mr3d_filter'.
+    """ Wrap the Sparse2d ``mr3d_filter``.
     """
     # WARNING: relative path with ~  doesn't work, use absolute path from /home
     # Generate the command
@@ -272,7 +272,7 @@ def mr2d1d_trans(
         in_image, out_image,
         type_of_multiresolution_transform=14, number_of_scales_2D=5,
         number_of_scales_1D=4, normalize=False, verbose=False, reverse=False):
-    """ Wrap the Sparse2d 'mr21d_trans'.
+    """ Wrap the Sparse2d ``mr21d_trans``.
     """
     # WARNING: relative path with ~  doesn't work, use absolute path from /home
     # Generate the command
diff --git a/pysap/extensions/transform.py b/pysap/extensions/transform.py
index 182ff21f..3979b408 100644
--- a/pysap/extensions/transform.py
+++ b/pysap/extensions/transform.py
@@ -13,17 +13,17 @@
 Available 2D transform from ISAP are:
 
 - to get the full list of builtin wavelets' names just use the pysap.wavelist
-  with 'isap-2d' as the family argument.
+  with ``isap-2d`` as the family argument.
 
 Available 3D transform from ISAP are:
 
 - to get the full list of builtin wavelets' names just use the pysap.wavelist
-  with 'isap-3d' as the family argument.
+  with ``isap-3d`` as the family argument.
 
 Available transform from pywt are:
 
 - to get the full list of builtin wavelets' names just use the pysap.wavelist
-  with 'pywt' as the family argument.
+  with ``pywt`` as the family argument.
 """
 
 # System import
@@ -61,7 +61,7 @@ def __init__(self, nb_scale, verbose=0, dim=2, is_decimated=True,
 
         Parameters
         ----------
-        data: ndarray
+        data: numpy.ndarray
             the input data.
         nb_scale: int
             the number of scale of the decomposition that includes the
@@ -104,15 +104,14 @@ def _analysis(self, data, **kwargs):
 
         Parameters
         ----------
-        data: nd-array
+        data: numpy.ndarray
             a real array to be decomposed.
 
         Returns
         -------
-        analysis_data: nd_array
-            the decomposition coefficients.
-        analysis_header: dict
-            the decomposition associated information.
+        numpy.ndarray
+            The decomposition coefficients and associated information
+
         """
         if self.is_decimated:
             coeffs = pywt.wavedecn(data, self.trf, mode=self.padding_mode,
@@ -131,14 +130,14 @@ def _synthesis(self, analysis_data, analysis_header):
 
         Parameters
         ----------
-        analysis_data: list of nd-array
-            the wavelet coefficients array.
+        analysis_data: list
+            the wavelet coefficients array
         analysis_header: dict
-            the wavelet decomposition parameters.
+            the wavelet decomposition parameters
 
         Returns
         -------
-        data: nd-array
+        numpy.ndarray
             the reconstructed data array.
         """
         coeffs = self._organize_pywt(analysis_data, analysis_header)
@@ -154,15 +153,14 @@ def _organize_pysap(self, coeffs):
 
         Parameters
         ----------
-        coeffs: list of dict or ndarray
+        coeffs: list or numpy.ndarray
             the pywt input coefficents.
 
         Returns
         -------
-        data: list of ndarray
-            the organized coefficients.
-        info: list
-            the pywt transform information.
+        tuple
+            the organized coefficients and the pywt transform information
+
         """
         if not isinstance(coeffs, list):
             coeffs = [coeffs]
@@ -186,14 +184,14 @@ def _organize_pywt(self, data, info):
 
         Parameters
         ----------
-        data: list of ndarray
+        data: list
             the organized coefficients.
         info: list
             the pywt transform information.
 
         Returns
         -------
-        coeffs: list
+        list
             the pywt input coefficents.
         """
         coeffs = []
@@ -220,10 +218,11 @@ def pywt_class_factory(func, name, destination_module_globals):
 
     Parameters
     ----------
-    func: @function
-        the wavelet transform function.
+    func: callable
+        the wavelet transform function
     name: str
-        the wavelet name we want to instanciate.
+        the wavelet name we want to instanciate
+
     """
     # Define the transform class name
     class_name = name.replace(".", "")
@@ -231,7 +230,7 @@ def pywt_class_factory(func, name, destination_module_globals):
     # Define the trsform class parameters
     class_parameters = {
         "__module__": destination_module_globals["__name__"],
-        "_id":  destination_module_globals["__name__"] + "." + class_name,
+        "_id": destination_module_globals["__name__"] + "." + class_name,
         "_pywt_name": name,
         "_pywt_func": func
     }
@@ -268,7 +267,7 @@ def __init__(self, nb_scale, verbose=0, dim=2, padding_mode="zero",
 
         Parameters
         ----------
-        data: ndarray
+        data: numpy.ndarray
             the input data.
         nb_scale: int
             the number of scale of the decomposition that includes the
@@ -327,18 +326,17 @@ def _analysis(self, data, **kwargs):
 
         Parameters
         ----------
-        data: nd-array
+        data: numpy.ndarray
             a real array to be decomposed.
         kwargs: dict (optional)
             the parameters that will be passed to
-            'pysap.extensions.mr_tansform'.
+            ``pysap.extensions.mr_tansform``.
 
         Returns
         -------
-        analysis_data: nd_array
-            the decomposition coefficients.
-        analysis_header: dict
-            the decomposition associated information.
+        numpy.ndarray
+            The decomposition coefficients and associated information
+
         """
         # Update ISAP parameters
         kwargs["type_of_multiresolution_transform"] = self.isap_transform_id
@@ -382,15 +380,16 @@ def _synthesis(self, analysis_data, analysis_header):
 
         Parameters
         ----------
-        analysis_data: list of nd-array
-            the wavelet coefficients array.
+        analysis_data: list
+            the wavelet coefficients array
         analysis_header: dict
-            the wavelet decomposition parameters.
+            the wavelet decomposition parameters
 
         Returns
         -------
-        data: nd-array
-            the reconstructed data array.
+        numpy.ndarray
+            the reconstructed data array
+
         """
         # Use subprocess to execute binaries
         if self.use_wrapping:
@@ -458,25 +457,30 @@ def undecimated(cls, nb_scale, iso_shape, nb_band):
 
         Returns
         -------
-        bands_names: list of str
-            the name of the different bands.
-        flatten_fct: int
-            a function used to reorganize the ISAP decomposition coefficients,
-            see 'pysap/extensions/formating.py' module for more details.
-        unflatten_fct: callable
-            a function used to reorganize the decomposition coefficients using
-            ISAP convention, see 'pysap/extensions/formating.py' module for
-            more details.
-        is_decimated: bool
-            True if the decomposition include a decimation of the
-            band number of coefficients.
-        nb_band_per_scale: ndarray (<nb_scale>, )
-            vector of int holding the number of band per scale.
-        bands_lengths: ndarray (<nb_scale>, max(<nb_band_per_scale>, 0))
-            array holding the length between two bands of the data
-            vector per scale.
-        bands_shapes: list of list of 2-uplet (<nb_scale>, <nb_band_per_scale>)
-            structure holding the shape of each bands at each scale.
+        tuple
+            bands_names: list of str
+                the name of the different bands.
+            flatten_fct: int
+                a function used to reorganize the ISAP decomposition
+                coefficients, see ``pysap/extensions/formating.py`` module for
+                more details.
+            unflatten_fct: callable
+                a function used to reorganize the decomposition coefficients
+                using ISAP convention, see ``pysap/extensions/formating.py``
+                module for more details.
+            is_decimated: bool
+                True if the decomposition include a decimation of the
+                band number of coefficients.
+            nb_band_per_scale: numpy.ndarray ``(<nb_scale>, )``
+                vector of int holding the number of band per scale.
+            bands_lengths: numpy.ndarray
+                ``(<nb_scale>, max(<nb_band_per_scale>, 0))``
+                array holding the length between two bands of the data
+                vector per scale.
+            bands_shapes: list of list of 2-uplet
+                ``(<nb_scale>, <nb_band_per_scale>)``
+                structure holding the shape of each bands at each scale.
+
         """
         if nb_band == 1:
             bands_names = ["a"]
@@ -517,25 +521,30 @@ def decimated(cls, nb_scale, iso_shape, nb_band, scale_shift=0):
 
         Returns
         -------
-        bands_names: list of str
-            the name of the different bands.
-        flatten_fct: int
-            a function used to reorganize the ISAP decomposition coefficients,
-            see 'pysap/extensions/formating.py' module for more details.
-        unflatten_fct: callable
-            a function used to reorganize the decomposition coefficients using
-            ISAP convention, see 'pysap/extensions/formating.py' module for
-            more details.
-        is_decimated: bool
-            True if the decomposition include a decimation of the
-            band number of coefficients.
-        nb_band_per_scale: ndarray (<nb_scale>, )
-            vector of int holding the number of band per scale.
-        bands_lengths: ndarray (<nb_scale>, max(<nb_band_per_scale>, 0))
-            array holding the length between two bands of the data
-            vector per scale.
-        bands_shapes: list of list of 2-uplet (<nb_scale>, <nb_band_per_scale>)
-            structure holding the shape of each bands at each scale.
+        tuple
+            bands_names: list of str
+                the name of the different bands.
+            flatten_fct: int
+                a function used to reorganize the ISAP decomposition
+                coefficients, see ``pysap/extensions/formating.py`` module for
+                more details.
+            unflatten_fct: callable
+                a function used to reorganize the decomposition coefficients
+                using ISAP convention, see ``pysap/extensions/formating.py``
+                module for more details.
+            is_decimated: bool
+                True if the decomposition include a decimation of the
+                band number of coefficients.
+            nb_band_per_scale: numpy.ndarray ``(<nb_scale>, )``
+                vector of int holding the number of band per scale.
+            bands_lengths: numpy.ndarray
+                ``(<nb_scale>, max(<nb_band_per_scale>, 0))``
+                array holding the length between two bands of the data
+                vector per scale.
+            bands_shapes: list of list of 2-uplet
+                ``(<nb_scale>, <nb_band_per_scale>)``
+                structure holding the shape of each bands at each scale.
+
         """
         if nb_band == 1:
             bands_names = ["a"]
diff --git a/pysap/info.py b/pysap/info.py
index 7c1026c2..3f3b025e 100644
--- a/pysap/info.py
+++ b/pysap/info.py
@@ -55,13 +55,8 @@
 DOWNLOAD_URL = 'https://github.com/CEA-COSMIC/pysap'
 LICENSE = 'CeCILL-B'
 CLASSIFIERS = CLASSIFIERS
-AUTHOR = """
-Antoine Grigis <antoine.grigis@cea.fr>
-Samuel Farrens <samuel.farrens@cea.fr>
-Jean-Luc Starck <jl.stark@cea.fr>
-Philippe Ciuciu <philippe.ciuciu@cea.fr>
-"""
-AUTHOR_EMAIL = 'antoine.grigis@cea.fr'
+AUTHOR = 'The PySAP Team'
+AUTHOR_EMAIL = 'samuel.farrens@cea.fr'
 PLATFORMS = 'Linux,OSX'
 ISRELEASE = True
 VERSION = __version__
@@ -72,24 +67,9 @@
     'matplotlib>=3.3.4',
     'astropy>=4.1',
     'nibabel>=3.2.1',
-    'pyqtgraph>=0.11.1',
     'progressbar2>=3.53.1',
     'modopt>=1.5.1',
     'scikit-learn>=0.24.1',
+    'scikit-image>=0.17.2',
     'PyWavelets>=1.1.1',
 ]
-PREINSTALL_REQUIRES = [
-    'pybind11==2.6.2',
-    'pyqt5==5.15.4',
-]
-EXTRA_REQUIRES = {
-    'gui': {
-        'PySide>=1.2.2',
-        # 'python-pypipe>=0.0.1'
-    }
-}
-PLUGINS = [
-    'pysap-astro==0.0.1',
-    'pysap-mri==0.4.0',
-    'pysap-etomo==0.0.1',
-]
diff --git a/pysap/plotting/image.py b/pysap/plotting/image.py
index 6f22518d..13ccd8da 100644
--- a/pysap/plotting/image.py
+++ b/pysap/plotting/image.py
@@ -9,8 +9,14 @@
 
 # System import
 import numpy
-from pyqtgraph.Qt import QtGui
-import pyqtgraph
+
+try:
+    import pyqtgraph
+    from pyqtgraph.Qt import QtGui
+except ImportError:  # pragma: no cover
+    PYQT_FOUND = False
+else:
+    PYQT_FOUND = True
 
 
 def plot_data(data, scroll_axis=2):
@@ -19,11 +25,23 @@ def plot_data(data, scroll_axis=2):
 
     Parameters
     ----------
-    data: array
+    data: numpy.ndarray
         the data to be displayed.
     scroll_axis: int (optional, default 2)
         the scroll axis for 3d data.
+
+    Notes
+    -----
+    This function is deprecated and will be removed in a future release.
+
     """
+    if not PYQT_FOUND:
+        raise ImportError(
+            'To use this deprecated function you will need to install '
+            + 'pyqtgraph manually. Note that the current implementation '
+            + 'is not compatible with PyQT v6.'
+        )
+
     # Check input parameters
     if data.ndim not in range(1, 4):
         raise ValueError("Unsupported data dimension.")
diff --git a/pysap/plotting/transform.py b/pysap/plotting/transform.py
index 0a3cd3cc..9c52c8c9 100644
--- a/pysap/plotting/transform.py
+++ b/pysap/plotting/transform.py
@@ -9,23 +9,41 @@
 
 # System import
 import matplotlib.pyplot as plt
-from pyqtgraph.Qt import QtGui
-import pyqtgraph
 import numpy
 
+try:
+    import pyqtgraph
+    from pyqtgraph.Qt import QtGui
+except ImportError:  # pragma: no cover
+    PYQT_FOUND = False
+else:
+    PYQT_FOUND = True
+
 
 def plot_transform(transform, scales=None, multiview=False):
-    """ Display the different bands on the requested scales.
+    """Display the different bands on the requested scales.
 
     Parameters
     ----------
-    transform: WaveletTransformBase derived instance
+    transform: WaveletTransformBase
         a wavelet decomposition.
-    scales: list of int, default None
+    scales: list, default None
         the desired scales, if None compute at all scales.
     multiview: bool, default False
         if True use a slider to select a specific band.
+
+    Notes
+    -----
+    This function is deprecated and will be removed in a future release.
+
     """
+    if not PYQT_FOUND:
+        raise ImportError(
+            'To use this deprecated function you will need to install '
+            + 'pyqtgraph manually. Note that the current implementation '
+            + 'is not compatible with PyQT v6.'
+        )
+
     # Set default scales
     scales = scales or range(transform.nb_scale)
 
diff --git a/setup.cfg b/setup.cfg
index b2f131d7..f12574a1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,12 +1,3 @@
-[nosetests]
-verbosity=2
-nologcapture=0
-cover-package=pysap
-with-coverage=1
-cover-xml=1
-with-xunit=1
-ignore-files=test_examples.py
-
 [build_sphinx]
 source-dir=doc
 build-dir=doc/build
@@ -18,3 +9,13 @@ repository=http://pypi.python.org/pypi
 
 [metadata]
 description-file = README.rst
+
+[tool:pytest]
+testpaths = pysap
+addopts =
+  --verbose
+  --pycodestyle
+  --cov=./
+  --cov-report=term
+  --cov-report=xml
+  --junitxml=pytest.xml
diff --git a/setup.py b/setup.py
index eaf9bdc9..9266321d 100644
--- a/setup.py
+++ b/setup.py
@@ -40,6 +40,11 @@
     os.path.join('pysap', 'apps', 'pysapview3')
 ]
 
+# Source PySAP plug-ins
+this_directory = os.path.abspath(os.path.dirname(__file__))
+with open(os.path.join(this_directory, 'plugins.txt')) as f:
+    pysap_plugins = f.read().splitlines()
+
 # Workaround
 rm_args = []
 if '--release' in sys.argv:
@@ -78,10 +83,10 @@ def check_plugins(plugin_list):
     if not isinstance(plugin_list, list):
         raise TypeError('Plug-in list must be of type list.')
 
-    pysap_plugins = dict([
-        _plugin.split('==') for _plugin in release_info['PLUGINS']
+    plugins_dict = dict([
+        _plugin.split('==') for _plugin in pysap_plugins
     ])
-    allowed_plugins = pysap_plugins.keys()
+    allowed_plugins = plugins_dict.keys()
 
     only_pinned = []
     for plugin in plugin_list:
@@ -92,7 +97,7 @@ def check_plugins(plugin_list):
                 + '{0}\n'.format(list(allowed_plugins))
             )
         only_pinned.append(
-            '{0}=={1}'.format(plugin, pysap_plugins[plugin])
+            '{0}=={1}'.format(plugin, plugins_dict[plugin])
         )
 
     return only_pinned
@@ -113,7 +118,7 @@ def pipinstall(package_list):
 def install_plugins():
     """Install Plug-Ins."""
 
-    plugin_list = release_info['PLUGINS']
+    plugin_list = pysap_plugins
     if only_plugins:
         plugin_list = check_plugins(only_plugins)
     elif no_plugins:
@@ -142,21 +147,10 @@ def __init__(self, name, sourcedir=''):
 class CMakeBuild(build_ext):
     """Define a cmake build extension."""
 
-    def _set_pybind_path(self):
-        """Set path to Pybind11 include directory."""
-
-        self.pybind_path = getattr(import_module('pybind11'), 'get_include')()
-
     def run(self):
-        """Redifine the run method."""
+        """Redefine the run method."""
 
-        # Preinstall packages
-        pipinstall(release_info['PREINSTALL_REQUIRES'])
-
-        # Set Pybind11 path
-        self._set_pybind_path()
-
-        # Check cmake is installed and is sufficiently new.
+        # Check that an appropriate version of cmake is installed.
         try:
             out = subprocess.check_output(['cmake', '--version'])
         except OSError:
@@ -167,8 +161,8 @@ def run(self):
         cmake_version = LooseVersion(
             re.search(r'version\s*([\d.]+)', out.decode()).group(1)
         )
-        if cmake_version < '3.0.0':
-            raise RuntimeError('CMake >= 3.0.0 is required.')
+        if cmake_version < '3.12.0':
+            raise RuntimeError('CMake >= 3.12.0 is required.')
 
         # Build extensions
         for ext in self.extensions:
@@ -178,11 +172,11 @@ def build_extension(self, ext):
         """Build extension with cmake."""
         # Define cmake arguments
         extdir = os.path.abspath(
-            os.path.dirname(self.get_ext_fullpath(ext.name)))
+            os.path.dirname(self.get_ext_fullpath(ext.name))
+        )
         cmake_args = [
             '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,
             '-DPYTHON_EXECUTABLE=' + sys.executable,
-            '-DPYBIND11_INCLUDE_DIR=' + self.pybind_path
         ]
         cfg = 'Debug' if self.debug else 'Release'
         build_args = ['--config', cfg]
@@ -283,7 +277,6 @@ def run(self):
     url=release_info['URL'],
     packages=find_packages(exclude='doc'),
     platforms=release_info['PLATFORMS'],
-    extras_require=release_info['EXTRA_REQUIRES'],
     install_requires=release_info['REQUIRES'],
     package_data=pkgdata,
     scripts=scripts,
diff --git a/singularity/singularity_recipe.def b/singularity/singularity_recipe.def
deleted file mode 100644
index fed08823..00000000
--- a/singularity/singularity_recipe.def
+++ /dev/null
@@ -1,59 +0,0 @@
-Bootstrap: docker
-From: ubuntu:16.04
-
-
-%help
-    Singularity container for pySAP & Python3.
-
-    Contains:
-    - astropy
-    - ipython
-    - matplotlib
-    - modopt
-    - nibabel
-    - nilearn
-    - numpy
-    - progressbar2
-    - pyNFFT
-    - pyqtgraph
-    - python-pySAP
-    - PyWavelets
-    - scikit-learn
-    - scipy
-
-%labels
-    Maintainer Antoine Grigis <antoine.grigis@cea.fr>
-    Version v1.0.0
-
-%runscript
-    echo "Executing command in singularity container using Python3..."
-    exec python3 "$@"
-
-%post
-    echo "Adding NeuroDebian..."
-    mkdir /scratch
-    apt-get update -y
-    apt install tree gnupg2 wget eatmydata nano -y 
-    wget -O- http://neuro.debian.net/lists/stretch.de-md.full | tee /etc/apt/sources.list.d/neurodebian.sources.list
-    while true; do
-	    apt-key adv --recv-keys --keyserver hkp://eu.pool.sks-keyservers.net:80 0xA5D32F012649A5A9 || continue
-	    break
-    done
-    apt-get update -y
-
-    echo "Adding Python3 tools..."
-    apt install python3-pip ipython3 -y
-
-    echo "Adding pysap..."
-    apt install cmake git python3-pyqt4 python3-tk -y
-    pip3 install python-pysap
-    #pip3 install "git+https://github.com/LElgueddari/pysap@elastic_net"
-
-    echo "Adding Python3 modules..."
-    apt install libnfft3-dev -y
-    pip3 install scikit-image joblib scikit-learn nilearn PyWavelets
-    pip3 install cython git+https://github.com/ghisvail/pyNFFT.git
-    
-%environment
-    export LC_ALL=C
-
diff --git a/sparse2d/python/CMakeLists.txt b/sparse2d/python/CMakeLists.txt
index e1b02cfd..b8e32f44 100644
--- a/sparse2d/python/CMakeLists.txt
+++ b/sparse2d/python/CMakeLists.txt
@@ -1,87 +1,23 @@
-cmake_minimum_required(VERSION 3.0)
-include(ExternalProject)
-include(FindPkgConfig)
+############
+# PySparse #
+############
 
-# Adding customized cmake module for external packages
-list(APPEND CMAKE_MODULE_PATH  "${CMAKE_SOURCE_DIR}/cmake/Modules/")
+# -------------- #
+# Project Set Up #
+# -------------- #
 
+cmake_minimum_required(VERSION 3.12.0)
 project(pysparse)
 
-  # Find default python libraries and interpreter
-  find_package(PythonInterp REQUIRED)
-  if(NOT DEFINED PYTHON_LIBRARIES OR NOT DEFINED PYTHON_INCLUDE_DIRS)
-    find_package(PythonLibs REQUIRED)
-  else()
-    message(STATUS "Using Python Lib: ${PYTHON_LIBRARIES}")
-    message(STATUS "Using Python Inc: ${PYTHON_INCLUDE_DIRS}")
-  endif()
-  include_directories(${PYTHON_INCLUDE_DIRS})
-  link_directories(${PYTHON_LIBRARY_DIRS})
+  # Include custom modules in CMake module path
+  list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules/")
 
-  # Include Pybind11 headers
-  message(STATUS "Using Pybind11 Inc: ${PYBIND11_INCLUDE_DIR}")
-  include_directories(${PYBIND11_INCLUDE_DIR})
+  # Load CMake tools
+  include(ExternalProject)
 
-  # Find OpenMP
-  if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
-    set(OpenMP_INCLUDE_PATH "/usr/local/include")
-    set(OpenMP_LIB_PATH "/usr/local/lib")
-    set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -lomp")
-    set(OpenMP_CXX_LIB_NAMES "omp")
-    set(OpenMP_CXX_LIBRARIES "${OpenMP_LIB_PATH}/libomp.dylib")
-    include_directories(${OpenMP_INCLUDE_PATH})
-    link_directories(${OpenMP_LIB_PATH})
-  else()
-    find_package(OpenMP REQUIRED)
-  endif()
-
-  # Flags for MAC OSX
-  if(APPLE)
-    set(CMAKE_MACOSX_RPATH 1)
-    set(APPLE_FLAGS "-DMACOS")
-  else(APPLE)
-    set(APPLE_FLAGS "")
-  endif(APPLE)
-
-  # Compilation flags
-  set(CMAKE_CXX_FLAGS "-std=c++11 -DNO_DISP_IO -ggdb3 -fPIC -O2 -ffast-math -fomit-frame-pointer ${APPLE_FLAGS} ${OpenMP_CXX_FLAGS} -Wno-write-strings -DNDEBUG")
-
-  # Custom modules
-  include(BuildCfitsIO)
+  # Set install directory for Sparse2D binaries
   get_filename_component(SPARSE2D_INSTALL_DIR ${PYTHON_EXECUTABLE} DIRECTORY)
   message(STATUS "Binary output path : ${SPARSE2D_INSTALL_DIR}")
-  include(BuildSparse2D)
-
-  # Includes
-  include_directories(${PYTHON_INCLUDE_DIRS})
-  include_directories(${cfitsio_INCLUDE_DIR})
-  include_directories(${sparse2d_INCLUDE_DIR})
-  include_directories(${OpenMP_INCLUDE_PATH})
-  link_directories(${cfitsio_LIBRARY_DIR})
-  link_directories(${sparse2d_LIBRARY_DIR})
-
-
-  set(EXAMPLES pysparse)
 
-  # Build and link the module
-  foreach(program ${EXAMPLES})
-
-    add_library(${program} SHARED ${program}.cpp)
-
-    if(APPLE)
-      target_link_libraries(${program} ${OpenMP_CXX_LIBRARIES} ${sparse2d_LIBRARIES} ${cfitsio_LIBRARIES})
-    else(APPLE)
-      target_link_libraries(${program} ${OpenMP_CXX_LIBRARIES} ${sparse2d_LIBRARIES} ${cfitsio_LIBRARIES} ${PYTHON_LIBRARIES})
-    endif(APPLE)
-
-    add_dependencies(${program} cfitsio)
-    add_dependencies(${program} sparse2d)
-
-    set_target_properties(${program} PROPERTIES SUFFIX .so)
-    set_target_properties(${program} PROPERTIES PREFIX "")
-
-    if(APPLE)
-      set_target_properties(${program} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
-    endif(APPLE)
-
-  endforeach(program)
+  # Build Sparse2D
+  include(BuildSparse2D)
diff --git a/sparse2d/python/cmake/Modules/BuildCfitsIO.cmake b/sparse2d/python/cmake/Modules/BuildCfitsIO.cmake
deleted file mode 100644
index 284c9b66..00000000
--- a/sparse2d/python/cmake/Modules/BuildCfitsIO.cmake
+++ /dev/null
@@ -1,25 +0,0 @@
-#========================================================#
-# Build the CfitsIO dependencies for the project         #
-#========================================================#
-
-set(cfitsioVersion 3.410)
-set(cfitsioSHA256 a556ac7ea1965545dcb4d41cfef8e4915eeb8c0faa1b52f7ff70870f8bb5734c)
-
-string(REPLACE "." "" cfitsioFolderName ${cfitsioVersion})
-
-ExternalProject_Add(cfitsio
-    PREFIX cfitsio
-    URL  http://heasarc.gsfc.nasa.gov/FTP/software/fitsio/c/cfitsio${cfitsioFolderName}.tar.gz
-    URL_HASH  SHA256=${cfitsioSHA256}
-    CONFIGURE_COMMAND ./configure
-        --prefix=${CMAKE_BINARY_DIR}/extern
-    BUILD_COMMAND make install
-        -j8
-    INSTALL_COMMAND ""
-    BUILD_IN_SOURCE 1
-    )
-
-set(cfitsio_LIBRARY_DIR ${CMAKE_BINARY_DIR}/extern/lib/ )
-set(cfitsio_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extern/include/ )
-set(cfitsio_LIBRARIES -lcfitsio)
-
diff --git a/sparse2d/python/cmake/Modules/BuildSparse2D.cmake b/sparse2d/python/cmake/Modules/BuildSparse2D.cmake
index cfb06e3b..6a8a62af 100644
--- a/sparse2d/python/cmake/Modules/BuildSparse2D.cmake
+++ b/sparse2d/python/cmake/Modules/BuildSparse2D.cmake
@@ -1,27 +1,23 @@
-#========================================================#
-# Build the CfitsIO dependencies for the project         #
-#========================================================#
+#=================================================#
+# Build the Sparse2D dependencies for the project #
+#=================================================#
 
-set(sparse2dVersion 2.1.4)
+# Set Sparse2D Version
+set(sparse2dVersion 3.0.0)
 
+# Download and build Sparse2D
 ExternalProject_Add(sparse2d
     PREFIX sparse2d
     GIT_REPOSITORY https://github.com/CosmoStat/Sparse2D.git
-    GIT_TAG v2.1.4
-    # GIT_TAG master
-    DEPENDS cfitsio
+    # GIT_TAG v${sparse2dVersion}
+    GIT_TAG master
     CONFIGURE_COMMAND cmake ../sparse2d
         -DCMAKE_INSTALL_PREFIX=${SPARSE2D_INSTALL_DIR}/..
-        -DCFITSIO_INCLUDE_DIRS=${cfitsio_INCLUDE_DIR}
-        -DCFITSIO_LIBRARY_DIRS=${cfitsio_LIBRARY_DIR}
-        -DCFITSIO_LIBRARIES=${cfitsio_LIBRARIES}
-        -DCMAKE_CXX_FLAGS="-std=c++14"
         -DCMAKE_BUILD_TYPE=RELEASE
-    BUILD_COMMAND make install
-        -j8
+        -DONLY_SPARSE=ON
+        -DUSE_FFTW=OFF
+        -DBUILD_CFITSIO=ON
+        -DPYBIND_INSTALL_PATH=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
+    BUILD_COMMAND make install -j8
     BUILD_IN_SOURCE 0
-    )
-
-set(sparse2d_LIBRARY_DIR ${SPARSE2D_INSTALL_DIR}/../lib/ )
-set(sparse2d_INCLUDE_DIR ${SPARSE2D_INSTALL_DIR}/../include/ )
-set(sparse2d_LIBRARIES -lmga2d -lsparse3d -lsparse2d -lsparse1d -ltools)
+)
diff --git a/sparse2d/python/deconvolve.hpp b/sparse2d/python/deconvolve.hpp
deleted file mode 100644
index b336333b..00000000
--- a/sparse2d/python/deconvolve.hpp
+++ /dev/null
@@ -1,332 +0,0 @@
-#ifndef DECONVOLVE_H_
-#define DECONVOLVE_H_
-
-#include <iostream>
-
-#include "sparse2d/IM_Obj.h"
-#include "sparse2d/IM_IO.h"
-#include "sparse2d/MR_Obj.h"
-#include "sparse2d/IM_Deconv.h"
-#include "sparse2d/MR_Deconv.h"
-#include "sparse2d/MR_Sigma.h"
-#include "sparse2d/MR_Sigma.h"
-#include "filter.hpp"
-
-#define NBR_OK_METHOD 5
-
-
-class MRDeconvolve
-{
-    public:
-        MRDeconvolve(int type_of_deconvolution = 3,
-                int type_of_multiresolution_transform = 2,
-                int type_of_filters = 1,
-                int number_of_undecimated_scales = -1,
-                float sigma_noise = 0.,
-                int type_of_noise = 1,
-                int number_of_scales = DEFAULT_NBR_SCALE,
-                float nsigma = DEFAULT_N_SIGMA,
-                int number_of_iterations = DEFAULT_MAX_ITER_DECONV,
-                float epsilon = DEFAULT_EPSILON_DECONV,
-                bool psf_max_shift = true,
-                bool verbose = false,
-                bool optimization=false,
-                float fwhm_param = 0.,
-                float convergence_param = 1.,
-                float regul_param = 0.,
-                std::string first_guess = "",
-                std::string icf_filename = "",
-                std::string rms_map = "",
-                bool kill_last_scale=false,
-                bool positive_constraint=true,
-                bool keep_positiv_sup=false,
-                bool sup_isol=false,
-                float pas_codeur = 1, 
-                float sigma_gauss = 0,
-                float mean_gauss = 0);
-
-                void Info();
-                py::array_t<float> Deconvolve(py::array_t<float>& arr, py::array_t<float>& psf);
-                void DeconvInit();
-                void NoiseModelInit();
-
-    private:
-        float convergence_param;
-        float regul_param;
-        int number_of_undecimated_scales;
-        std::string first_guess;
-        std::string icf_filename;
-        std::string rms_map;
-        bool kill_last_scale;
-        bool positive_constraint;
-        bool keep_positiv_sup;
-        bool sup_isol;
-        float sigma_noise;
-        float nsigma;
-        int number_of_iterations;
-        bool psf_max_shift;
-        bool verbose;
-        bool optimization;
-        float fwhm_param;
-        float pas_codeur;
-        float sigma_gauss;
-        float mean_gauss;
-        int number_of_scales;
-        float epsilon;
-
-        MRDeconv CDec;
-        MRNoiseModel model_data;
-        bool gauss_conv = false;
-        bool use_nsigma = false;
-        type_sb_filter sb_filter = F_MALLAT_7_9;
-        type_noise stat_noise = DEFAULT_STAT_NOISE;
-        type_transform transform = DEFAULT_TRANSFORM;
-        type_deconv deconv = DEFAULT_DECONV;
-        type_deconv TabMethod[NBR_OK_METHOD] = {DEC_MR_CITTERT,DEC_MR_GRADIENT,
-                            DEC_MR_LUCY, DEC_MR_MAP,DEC_MR_VAGUELET};
-
-};
-
-MRDeconvolve::MRDeconvolve(int type_of_deconvolution, int type_of_multiresolution_transform,
-                int type_of_filters, int number_of_undecimated_scales, float sigma_noise,
-                int type_of_noise, int number_of_scales, float nsigma, int number_of_iterations,
-                float epsilon, bool psf_max_shift, bool verbose, bool optimization, 
-                float fwhm_param, float convergence_param, float regul_param, std::string first_guess,
-                std::string icf_filename, std::string rms_map, bool kill_last_scale,
-                bool positive_constraint, bool keep_positiv_sup, bool sup_isol,
-                float pas_codeur, float sigma_gauss, float mean_gauss)
-                {
-                    this->convergence_param = convergence_param;
-                    this->regul_param = regul_param;
-                    this->number_of_undecimated_scales = number_of_undecimated_scales;
-                    this->first_guess = first_guess;
-                    this->icf_filename = icf_filename;
-                    this->rms_map =rms_map;
-                    this->kill_last_scale = kill_last_scale;
-                    this->positive_constraint = positive_constraint;
-                    this->keep_positiv_sup = keep_positiv_sup;
-                    this->sup_isol = sup_isol;
-                    this->sigma_noise = sigma_noise;
-                    this->nsigma = nsigma;
-                    this->number_of_iterations = number_of_iterations;
-                    this->psf_max_shift = psf_max_shift;
-                    this->optimization = optimization;
-                    this->verbose = verbose;
-                    this->number_of_scales = number_of_scales;
-                    this->fwhm_param = fwhm_param;
-                    this->pas_codeur = pas_codeur;
-                    this->sigma_gauss = sigma_gauss;
-                    this->mean_gauss = mean_gauss;
-
-                    if (this->fwhm_param > 0)
-                        this->gauss_conv = True;
-                    
-                    if (this->gauss_conv && this->icf_filename != "")
-                        throw std::invalid_argument("Error: icf_filename and fwhm_param options are not compatible ..");
-
-                    if ((epsilon < 0) || (epsilon > 1.)) 
-                        this->epsilon = DEFAULT_EPSILON_DECONV;
-                    else
-                        this->epsilon = epsilon;
-
-                    if (type_of_deconvolution <= 0 || type_of_deconvolution > NBR_OK_METHOD)
-                        throw std::invalid_argument("Error: bad type of deconvolution: " + std::to_string(type_of_deconvolution));
-                    else if (type_of_deconvolution != 3)                
-		                this->deconv  = (type_deconv) (TabMethod[type_of_deconvolution-1]);
-
-                    if (type_of_noise > 0 && type_of_noise < NBR_NOISE && type_of_noise != 1)
-                        this->stat_noise = (type_noise) (type_of_noise-1);
-                    else if (type_of_noise != 1)
-                        throw std::invalid_argument("Error: bad type of noise: " + std::to_string(type_of_noise));
-                    
-                    if (type_of_multiresolution_transform != 2 &&
-                            type_of_multiresolution_transform > 0 &&
-                            type_of_multiresolution_transform <= NBR_TRANSFORM)
-                        this->transform = (type_transform)(type_of_multiresolution_transform-1);
-                    else if (type_of_multiresolution_transform != 2)
-                        throw std::invalid_argument("Error: bad type of transform: " + std::to_string(type_of_multiresolution_transform));
-                    
-                    if (type_of_filters != 1)
-                        this->sb_filter = get_filter_bank(int_to_char(type_of_filters));
-                    
-                    if (sigma_noise != 0.)
-                        this->stat_noise = NOISE_GAUSSIAN;
-                    
-                    if (this->sigma_gauss != 0 && this->pas_codeur != 1 && this->mean_gauss != 0)
-                    {
-                        this->stat_noise = NOISE_POISSON;
-                        this->sigma_noise = 1.;
-                    }
-                    if ((number_of_scales <= 1 || number_of_scales > MAX_SCALE) && number_of_scales != DEFAULT_NBR_SCALE)
-                        throw std::invalid_argument("Error: bad number of scales: ]1; " + std::to_string(MAX_SCALE) + "]");
-                    
-                    if (this->nsigma != DEFAULT_N_SIGMA)
-                    {
-                        this->use_nsigma = true;
-                        if (nsigma <= 0.)
-                            this->nsigma = DEFAULT_N_SIGMA;
-                    }
-                    if (number_of_iterations <= 0)
-                        this->number_of_iterations = DEFAULT_MAX_ITER_DECONV;
-
-                    if (this->regul_param  < 0.)
-                        this->regul_param = 0.1;
-
-                    if (type_of_deconvolution <= 0 || type_of_deconvolution > NBR_OK_METHOD)
-                        throw std::invalid_argument("Error: bad type of deconvolution: " + std::to_string(type_of_deconvolution));
-                
-
-                    if ((this->rms_map != "") && (this->stat_noise != NOISE_NON_UNI_ADD) && (this->stat_noise !=  NOISE_CORREL))
-                        throw std::invalid_argument(std::string("Error: this noise model is not correct when RMS map option is set.\n       Valid models are: \n        ") +
-                                                    + StringNoise(NOISE_NON_UNI_ADD) + "        " + StringNoise(NOISE_CORREL));
-                    
-                    if ((this->stat_noise ==  NOISE_CORREL) && (this->rms_map == ""))
-                        throw std::invalid_argument("Error: this noise model needs a noise map (rms_map option) ");
-	
-                    if ((isotrop(transform) == False)
-                            && ((this->stat_noise == NOISE_NON_UNI_ADD) ||
-                                (this->stat_noise  == NOISE_NON_UNI_MULT)))
-                        throw std::invalid_argument(std::string("Error: with this transform, non stationary noise models are not valid : ") + StringFilter(FILTER_THRESHOLD));
-                 
-                    if (this->kill_last_scale && this->optimization)
-                        throw std::invalid_argument("Error: kill_last_scale and optimization options are not compatible ... ");
-
-                    if (this->kill_last_scale && ((this->deconv == DEC_MR_LUCY) || (this->deconv ==DEC_MR_MAP)))
-                            throw std::invalid_argument("Error: kill_last_scale option cannot be used with this deconvolution method  ... ");
-	   
-	
-                    if ((this->number_of_iterations == DEFAULT_MAX_ITER_DECONV) && (this->deconv == DEC_MR_VAGUELET))
-                        this->number_of_iterations = 10;
-
-                    if ((this->transform != TO_UNDECIMATED_MALLAT) && (this->transform != TO_MALLAT) && (type_of_filters != 1))
-                        throw std::invalid_argument("Error: option type_of_filters is only valid with Mallat transform ...");
-
-                    if ((convergence_param == 1.) && (this->regul_param > 1.))
-                        this->convergence_param = 1. / (2. * this->regul_param);
-                }
-
-void MRDeconvolve::Info()
-{
-    cout << endl << endl << "PARAMETERS: " << endl << endl;
-    cout << "Transform = " << StringTransform(this->transform) << endl;
-    cout << "Number of scales = " << this->number_of_scales << endl;
-    if (this->stat_noise == NOISE_GAUSSIAN) {
-        cout << "Type of Noise = GAUSSIAN" << endl;
-        if (this->sigma_noise > 0)
-            cout << "Sigma Noise = " << this->sigma_noise << endl;
-    }
-    else {
-        cout << "Type of Noise = POISSON" << endl;
-        cout << "  Gain = " << this->pas_codeur << endl;
-        cout << "  Read-out Noise Sigma  = " << this->sigma_gauss << endl;
-        cout << "  Read-out Mean = " << this->mean_gauss << endl;
-    }
-    cout << "Deconv = " << StringDeconv(this->deconv) << endl;
-    cout << "N_Sigma = " << this->nsigma << endl;
-    cout << "Epsilon = " << this->epsilon << endl;
-    cout << "Max_Iter = " << this->number_of_iterations << endl;
-    cout << "Convergence paramter = " << this->convergence_param << endl;
-    if (this->kill_last_scale)
-        cout << "Kill last scale " << endl;
-    cout << "Fwhm = " << this->fwhm_param << endl;
-}
-
-void MRDeconvolve::DeconvInit()
-{
-    this->CDec.KillLastScale = (Bool)this->kill_last_scale;
-    this->CDec.PositivConstraint = (Bool)this->positive_constraint;
-    this->CDec.DecMethod = this->deconv;
-    this->CDec.PsfMaxShift = (Bool)this->psf_max_shift;
-    this->CDec.Noise_Ima = this->sigma_noise;
-    this->CDec.MaxIter = this->number_of_iterations;
-    this->CDec.EpsCvg = this->epsilon;
-    this->CDec.IterCvg = this->convergence_param;
-    this->CDec.GaussConv = (Bool)this->gauss_conv;
-    this->CDec.Fwhm = this->fwhm_param;
-    this->CDec.OptimParam = (Bool)this->optimization;
-    this->CDec.Verbose = (Bool)this->verbose;
-    this->CDec.RegulParam = this->regul_param;
-    this->CDec.StatNoise = this->stat_noise;
-}
-
-void MRDeconvolve::NoiseModelInit()
-{
-    int nbr_band = model_data.nbr_band();
-    model_data.OnlyPositivDetect = (Bool)this->keep_positiv_sup;
-
-    if (this->sigma_noise > FLOAT_EPSILON)
-        model_data.SigmaNoise = this->sigma_noise;
-
-    if (this->nsigma != DEFAULT_N_SIGMA)
-    {
-            for (int b = 0; b < nbr_band; b++)
-            model_data.NSigma[b] = this->nsigma;
-    }
-
-    model_data.NiterSigmaClip = 1;
-    model_data.SizeBlockSigmaNoise = DEFAULT_SIZE_BLOCK_SIG;
-    model_data.CCD_Gain = this->pas_codeur;
-    model_data.CCD_ReadOutSigma = this->sigma_gauss;
-    model_data.CCD_ReadOutMean = this->mean_gauss;
-
-    if (this->sup_isol)
-        model_data.SupIsol = True;
-
-    if (this->rms_map != "") {
-        model_data.UseRmsMap = True;
-        io_read_ima_float(to_char(this->rms_map), model_data.RmsMap);
-    }
-}
-
-py::array_t<float> MRDeconvolve::Deconvolve(py::array_t<float>& arr, py::array_t<float>& psf)
-{
-    Ifloat Guess, Ima_ICF;
-    Ifloat *Pt_G = NULL;
-    Ifloat *Pt_ICF = NULL;
-
-    //outputs information
-    if (this->verbose)
-        Info();
-
-    //read input image 
-    this->CDec.Imag = array2image_2d(arr);
-    this->CDec.Psf = array2image_2d(psf);
-
-    //read additional files
-    if (this->first_guess != "") {
-        io_read_ima_float(to_char(this->first_guess), Guess);
-        if (this->first_guess != "") Pt_G = &Guess;
-    }
-    if (this->icf_filename != "") {
-        io_read_ima_float(to_char(this->icf_filename), Ima_ICF);
-        Pt_ICF = &Ima_ICF;
-    }
-
-    //deconvolution class initialization
-    DeconvInit();
-
-    if (this->verbose)
-        cout << " Start the deconvolution ... " << endl;
-    
-    //noise model class initialization
-    FilterAnaSynt FAS;
-    FilterAnaSynt *PtrFAS = NULL;
-
-    if ((this->transform == TO_MALLAT) || (this->transform == TO_UNDECIMATED_MALLAT)) {
-        FAS.Verbose = (Bool)this->verbose;
-        FAS.alloc(this->sb_filter);
-        PtrFAS = &FAS;
-    }
-    model_data.alloc(this->stat_noise, CDec.Imag.nl(), CDec.Imag.nc(),this->number_of_scales,
-                        this->transform, PtrFAS, NORM_L1, this->number_of_undecimated_scales);
-    NoiseModelInit();
-    this->CDec.ModelData = &model_data;
-
-    //deconvolution
-    this->CDec.im_deconv(Pt_G, Pt_ICF);
-
-    return image2array_2d(this->CDec.Obj);
-}
-
-
-#endif
\ No newline at end of file
diff --git a/sparse2d/python/filter.hpp b/sparse2d/python/filter.hpp
deleted file mode 100644
index bab735ee..00000000
--- a/sparse2d/python/filter.hpp
+++ /dev/null
@@ -1,656 +0,0 @@
-#ifndef FILTER_H_
-#define FILTER_H_
-
-#include <sparse2d/IM_Obj.h>
-#include <sparse2d/IM_IO.h>
-#include <sparse2d/MR_Obj.h>
-#include <sparse2d/MR_Filter.h>
-#include <sparse2d/MR_NoiseModel.h>
-#include <sparse2d/MR_Abaque.h>
-#include <sparse2d/MR_Psupport.h>
-#include <sparse2d/NR.h>
-#include <sparse2d/MR_Threshold.h>
-
-#include <iomanip>
-#include <vector>
-#include <iostream>
-
-#include "numpydata.hpp"
-
-std::vector<float> v = {DEFAULT_N_SIGMA};
-class MRFilters
-{
-    public:
-        MRFilters(
-            int type_of_filtering=1,
-            int coef_detection_method=1,
-            float type_of_multiresolution_transform=2,
-            float type_of_filters = 1,
-            float type_of_non_orthog_filters=2,
-            int type_of_noise=1,
-            int number_of_scales = DEFAULT_NBR_SCALE,
-            float regul_param = 0.1,
-            double epsilon = DEFAULT_EPSILON_FILTERING,
-            double iter_max = DEFAULT_MAX_ITER_FILTER,
-            double max_inpainting_iter = DEFAULT_MAX_ITER_INPAINTING,
-            std::string support_file_name="",
-            float sigma_noise=0.,
-            std::string flat_image="",
-            std::string rms_map="",
-            bool missing_data=false,
-            bool keep_positiv_sup=false,
-            bool write_info_on_prob_map=false,
-            double epsilon_poisson = 1.00e-03,
-            float size_block=7,
-            float niter_sigma_clip=1,
-            float first_scale=1,
-            std::string mask_file_name="",
-            std::string prob_mr_file="",
-            int min_event_number=0,
-            std::string background_model_image="",
-            bool positive_recons_filter=false,
-            bool suppress_isolated_pixels=false,
-            bool verbose=false,
-            float number_undec = -1,
-            float pas_codeur = 1,
-            float sigma_gauss = 0., //always positive
-            float mean_gauss = 0., //always positive
-            bool old_poisson = false,
-            bool positiv_ima= DEF_POSITIV_CONSTRAINT,
-            bool max_ima = DEF_MAX_CONSTRAINT,
-            bool kill_last_scale = false,
-            bool write_threshold = false,
-            std::vector<float>& tab_n_sigma = v
-            );
-        
-        void Info();
-        void NoiseInit(Ifloat data);
-        MRFiltering filtering_init(MRFiltering CFilter, Ifloat data);
-        void write_on_prob_map(MRFiltering CFilter, Ifloat data);
-        py::array_t<float> Filter(py::array_t<float>& arr);
-        
-    private:
-        int type_of_filters;
-        int number_of_scales;
-        float regul_param;
-        double epsilon; //convergence parameter
-        double iter_max;
-        double max_inpainting_iter;
-        std::string support_file_name;
-        float sigma_noise;
-        std::string flat_image;
-        std::string rms_map;
-        bool missing_data;
-        bool keep_positiv_sup;
-        bool write_info_on_prob_map;
-        double epsilon_poisson;
-        float size_block;
-        float niter_sigma_clip;
-        float first_scale;
-        std::string mask_file_name;
-        std::string prob_mr_file;
-        int min_event_number;
-        std::string background_model_image;
-        float number_undec;
-        float pas_codeur;
-        float sigma_gauss;
-        float mean_gauss;
-        bool positiv_ima;
-        bool max_ima;
-        bool kill_last_scale;
-        bool verbose;
-        bool write_threshold;
-        bool old_poisson;
-        bool suppress_isolated_pixels;
-        bool positive_recons_filter;
-        std::vector<float>& tab_n_sigma = v;
-        float n_sigma = DEFAULT_N_SIGMA;
-        bool use_n_sigma = false;
-
-        type_noise stat_noise = DEFAULT_STAT_NOISE;
-        type_transform transform = DEFAULT_MR_TRANS;
-        type_filter filter = FILTER_THRESHOLD;
-        type_threshold threshold = DEF_THRESHOLD;
-        type_undec_filter undec_filter = DEF_UNDER_FILTER;
-        type_sb_filter sb_filter = F_MALLAT_7_9;
-        MultiResol mr;
-        MRNoiseModel model_data;
-        FilterAnaSynt fas;
-        FilterAnaSynt *ptrfas = NULL;
-};
-
-static inline char* int_to_char(int c)
-{
-    std::string temp_str = std::to_string(c);
-    char* char_type = new char[temp_str.length()];
-    strcpy(char_type, temp_str.c_str());
-    return char_type;
-}
-MRFilters::MRFilters(
-    
-            int type_of_filtering,
-            int coef_detection_method,
-            float type_of_multiresolution_transform,
-            float type_of_filters,
-            float type_of_non_orthog_filters,
-            int type_of_noise,
-            int number_of_scales,
-            float regul_param,
-            double epsilon,
-            double iter_max,
-            double max_inpainting_iter,
-            std::string support_file_name,
-            float sigma_noise,
-            std::string flat_image,
-            std::string rms_map,
-            bool missing_data,
-            bool keep_positiv_sup,
-            bool write_info_on_prob_map,
-            double epsilon_poisson,
-            float size_block,
-            float niter_sigma_clip,
-            float first_scale,
-            std::string mask_file_name,
-            std::string prob_mr_file,
-            int min_event_number,
-            std::string background_model_image,
-            bool positive_recons_filter,
-            bool suppress_isolated_pixels,
-            bool verbose,
-            float number_undec,
-            float pas_codeur,
-            float sigma_gauss,
-            float mean_gauss,
-            bool old_poisson,
-            bool positiv_ima,
-            bool max_ima,
-            bool kill_last_scale,
-            bool write_threshold,
-            std::vector<float>& tab_n_sigma
-            )
-{
-    
-    this->type_of_filters = type_of_filters;
-    this->number_of_scales = number_of_scales;
-    this->regul_param = regul_param;
-    this->epsilon = epsilon;
-    this->iter_max = iter_max;
-    this->support_file_name = support_file_name;
-    this->sigma_noise = sigma_noise;
-    this->flat_image = flat_image;
-    this->rms_map = rms_map;
-    this->missing_data = missing_data;
-    this->keep_positiv_sup = keep_positiv_sup;
-    this->write_info_on_prob_map = write_info_on_prob_map;
-    this->epsilon_poisson = epsilon_poisson;
-    this->size_block = size_block;
-    this->niter_sigma_clip = niter_sigma_clip;
-    this->first_scale = first_scale - 1;
-    this->mask_file_name = mask_file_name;
-    this->old_poisson = old_poisson;
-    this->prob_mr_file = prob_mr_file;
-    this->min_event_number = min_event_number;
-    this->background_model_image = background_model_image;
-    this->number_undec  = number_undec;
-    this->pas_codeur = pas_codeur;
-    this->sigma_gauss= sigma_gauss;
-    this->mean_gauss = mean_gauss;
-    this->max_inpainting_iter = max_inpainting_iter;
-    this->positiv_ima = positiv_ima;
-    this->max_ima = max_ima;
-    this->kill_last_scale = kill_last_scale;
-    this->verbose = verbose;
-    this->write_threshold = write_threshold;
-    this->suppress_isolated_pixels = suppress_isolated_pixels;
-    this->positive_recons_filter = positive_recons_filter;
-    this->tab_n_sigma[0] = tab_n_sigma[0];
-
-
-    for (int i = 1; i < tab_n_sigma.size(); ++i)
-        (this->tab_n_sigma).push_back(tab_n_sigma[i]); 
-
-    this->n_sigma = tab_n_sigma[0];
-    
-    if (this->n_sigma != DEFAULT_N_SIGMA || this->tab_n_sigma.size() > 1)
-        this->use_n_sigma = true;
-    
-    if (this->n_sigma <= 0.)
-        this->n_sigma = DEFAULT_N_SIGMA;
-
-    if ((coef_detection_method > 0) && (coef_detection_method <= NBR_THRESHOLD))
-        this->threshold = (type_threshold)(coef_detection_method-1);
-    else
-        throw std::invalid_argument("Error: bad type of detection.");
-
-    if (this->regul_param < 0.) this->regul_param = 0.1;
-
-    if ((type_of_filtering > 0) && (type_of_filtering <= NBR_FILTERING))
-        this->filter = (type_filter) (type_of_filtering-1);
-    else
-        throw std::invalid_argument("Error: bad type of filtering.");
-
-    if ((type_of_multiresolution_transform > 0) && (type_of_multiresolution_transform <= NBR_TRANSFORM))
-        this->transform = (type_transform) (type_of_multiresolution_transform-1);
-    else
-        throw std::invalid_argument("Error: bad type of transform.");
-
-    if (this->type_of_filters != 1)
-        this->sb_filter = get_filter_bank(int_to_char(this->type_of_filters));
-
-    if ((type_of_non_orthog_filters> 0) && (type_of_non_orthog_filters <= NBR_UNDEC_FILTER))
-         this->undec_filter = (type_undec_filter) (type_of_non_orthog_filters-1);
-    else
-        throw std::invalid_argument("Error: bad type of filters.");
-
-    if ((type_of_noise > 0) && (type_of_noise <= NBR_NOISE+1))
-        this->stat_noise = (type_noise) (type_of_noise-1);
-    else
-        throw std::invalid_argument("Error: bad type of noise.");
-
-    //check sigma noise
-    if (this->sigma_noise != 0.)
-        this->stat_noise = NOISE_GAUSSIAN;
-
-    // Check the number of scales
-    if ((this->number_of_scales <= 1) || (this->number_of_scales > MAX_SCALE))
-        throw std::invalid_argument("Error: bad number of scale ]1;"+std::to_string(MAX_SCALE)+"] ");
-
-    // Check iter_max
-    if (this->iter_max <= 0)
-        this->iter_max = DEFAULT_MAX_ITER_FILTER;
-
-    //check convergence parameter
-    if ((this->epsilon < 0.) || (this->epsilon > 1.))
-        this->epsilon = DEFAULT_EPSILON_FILTERING;
-    
-    //check epsilon poisson
-    if ((this->epsilon_poisson <= 0.) || (this->epsilon_poisson > MAX_EPSILON))
-        throw std::invalid_argument("Error: bad precision number. ["+std::to_string(MIN_EPSILON)+";"+std::to_string(MAX_EPSILON)+"]");
-    
-    //check sizeblock
-    if (this->size_block < 2)
-        throw std::invalid_argument("Error: bad  SizeBlock parameter. SizeBlock > 1");
-
-    //check NiterClip
-    if (this->niter_sigma_clip < 1)
-        throw std::invalid_argument("Error: bad NiterClip parameter. NiterClip > 0 ");
-    //check first scale
-    if (this->first_scale < 0)
-        throw std::invalid_argument("Error: bad FirstScale parameter. FirstScale > 0");
-    
-    if (this->mask_file_name != "")
-        this->missing_data = true;
-
-    if (pas_codeur != 1 && mean_gauss != 0. && sigma_gauss != 0.) {
-        this->stat_noise = NOISE_POISSON;
-        this->sigma_noise = 1.;
-    }
-
-    if (!this->use_n_sigma && this->threshold == T_FDR) {
-        this->n_sigma = 2.;
-        this->use_n_sigma = true;
-    }
-    
-    if (this->stat_noise == NOISE_EVENT_POISSON) {
-        if (this->transform != TO_PAVE_BSPLINE) {
- 	      cerr << "WARNING: with this noise model, only the BSPLINE A TROUS can be used ... " << endl;
- 	      cerr << "        Type transform is set to: BSPLINE A TROUS ALGORITHM " << endl;
- 	   }
- 	   this->transform = TO_PAVE_BSPLINE;
-        if (this->number_of_scales == DEFAULT_NBR_SCALE)
- 	        this->number_of_scales = DEF_N_SCALE;
- 	   if (this->epsilon == DEFAULT_EPSILON_FILTERING)
-            this->epsilon= DEF_EPS_EVENT_FILTERING;
-    }
-
-    if (this->stat_noise == NOISE_CORREL && this->rms_map == "")
-        throw std::invalid_argument("Error: this noise model need a noise map (rms_map option)");
-
- 	if (this->rms_map != "" && this->stat_noise != NOISE_NON_UNI_ADD && this->stat_noise !=  NOISE_CORREL)
- 	   throw std::invalid_argument("Error: this noise model is not correct when RMS map option is set.");
-
-
-	if (((SetTransform(this->transform) == TRANSF_MALLAT) ||
-	       (SetTransform(this->transform) == TRANSF_FEAUVEAU))
- 	      && ((this->stat_noise == NOISE_NON_UNI_ADD) ||
- 	         (this->stat_noise  == NOISE_NON_UNI_MULT)))
-  	    throw std::invalid_argument("  Error: with this transform, non stationary noise models are not valid : ");
-
-
-       // isolated pixel removal is not valid with non isotropic transform.
-    if ((isotrop(this->transform) != True)
-            && (suppress_isolated_pixels == True)
-            && (this->transform != TO_UNDECIMATED_MALLAT)
-            && (this->transform != TO_UNDECIMATED_NON_ORTHO))
-        throw std::invalid_argument("Error: option suppress_isolated_pixels is not valid with non isotropic transform.");
-
-
-       // Epsilon option and MaxIter option are valid only with iterative methods
-    if ((this->epsilon != DEFAULT_EPSILON_FILTERING) || (this->iter_max != DEFAULT_MAX_ITER_FILTER)) {   
-       if ((this->filter != FILTER_ITER_THRESHOLD) && (this->filter != FILTER_ITER_ADJOINT_COEFF) &&
-            (this->filter != FILTER_WAVELET_CONSTRAINT) && (this->filter != FILTER_TV_CONSTRAINT))
-            throw std::invalid_argument("Error: option -e and -i are not valid with non iterative filtering methods.");
-    }
-
-    if ((this->epsilon_poisson == 1.00e-03) && (this->use_n_sigma))
-    {
-        if (old_poisson)
-	        this->epsilon_poisson = (1. - erf((double) this->n_sigma / sqrt((double) 2.))) / 2;
-	    else
-	        this->epsilon_poisson = erffc( (double) this->n_sigma / sqrt((double) 2.) ) / 2.;
-
-	    if ((this->stat_noise == NOISE_EVENT_POISSON) && (this->n_sigma > 12))
-	        throw std::invalid_argument("Error: n_sigma must be set to a lower value (<12).");
-    }
- 	if ((this->transform != TO_UNDECIMATED_MALLAT) && (this->transform != TO_MALLAT) && (type_of_filters != 1)) //Type_of_filters has been set
-        throw std::invalid_argument("Error: option type_of_filters is only valid with Mallat transform");
-}
-
-
-
-static inline double dierfc(double y)
-{
-    double s, t, u, w, x, z;
-
-    z = y;
-    if (y > 1)
-        z = 2 - y;
-    w = 0.916461398268964 - log(z);
-    u = sqrt(w);
-    s = (log(u) + 0.488826640273108) / w;
-    t = 1 / (u + 0.231729200323405);
-    x = u * (1 - s * (s * 0.124610454613712 + 0.5)) -
-        ((((-0.0728846765585675 * t + 0.269999308670029) * t +
-        0.150689047360223) * t + 0.116065025341614) * t +
-        0.499999303439796) * t;
-    t = 3.97886080735226 / (x + 3.97886080735226);
-    u = t - 0.5;
-    s = (((((((((0.00112648096188977922 * u +
-        1.05739299623423047e-4) * u - 0.00351287146129100025) * u -
-        7.71708358954120939e-4) * u + 0.00685649426074558612) * u +
-        0.00339721910367775861) * u - 0.011274916933250487) * u -
-        0.0118598117047771104) * u + 0.0142961988697898018) * u +
-        0.0346494207789099922) * u + 0.00220995927012179067;
-    s = ((((((((((((s * u - 0.0743424357241784861) * u -
-        0.105872177941595488) * u + 0.0147297938331485121) * u +
-        0.316847638520135944) * u + 0.713657635868730364) * u +
-        1.05375024970847138) * u + 1.21448730779995237) * u +
-        1.16374581931560831) * u + 0.956464974744799006) * u +
-        0.686265948274097816) * u + 0.434397492331430115) * u +
-        0.244044510593190935) * t -
-        z * exp(x * x - 0.120782237635245222);
-    x += s * (x * s + 1);
-    if (y > 1)
-        x = -x;
-    return x;
-}
-static inline char* to_char(std::string str) {
-    return const_cast<char*>(str.c_str());
-}
-
-void MRFilters::NoiseInit(Ifloat data)
-{
-    int i, s;
-    if ((this->transform == TO_MALLAT) || (this->transform == TO_UNDECIMATED_MALLAT)) {
-        fas.Verbose = (Bool)this->verbose;
-        fas.alloc(this->sb_filter);
-        ptrfas = &fas;
-    }
-
-    model_data.set_old_poisson((Bool)this->old_poisson);
-    model_data.write_threshold((Bool)this->write_threshold);
-    model_data.alloc(this->stat_noise, data.nl(), data.nc(), 
-                    this->number_of_scales, this->transform, ptrfas, NORM_L1, this->number_undec);
-
-    float number_band = model_data.nbr_band();
-    
-    if (this->sigma_noise > FLOAT_EPSILON)
-        model_data.SigmaNoise = this->sigma_noise;
-    if (this->use_n_sigma)
-    {
-        double n_sigma_12 = erffc( (double) 12. / sqrt((double) 2.) ) / 2.;
-    	if (this->verbose)
-            cout << " NbrTabNSigma = " << this->tab_n_sigma.size() << endl;
- 
-    	if (this->tab_n_sigma.size() == 1)
-        {
-            for (i = 0; i < number_band; i++)
-                model_data.NSigma[i] = this->n_sigma;
-        }
-        else
-        {
-        	  for (i = 0; i < this->tab_n_sigma.size() ; i++)
-                model_data.NSigma[i] = this->tab_n_sigma[i];
-        	  for (i = this->tab_n_sigma.size() ; i < number_band; i++)
-                model_data.NSigma[i] = this->tab_n_sigma[this->tab_n_sigma.size()-1];
-        }
-        for (s = 0; s < number_band; s++)
-            model_data.TabEps[s] = (model_data.NSigma[s] < 12) ? erffc( (double)  model_data.NSigma[s] / sqrt((double) 2.) ) / 2.: n_sigma_12;
-
-    }
-    
-    else for (s = 0; s < number_band; s++) model_data.TabEps[s] = this->epsilon_poisson;
-    if (this->n_sigma  == 111) {
-        for (i=0; i < 4; i++)
-            model_data.NSigma[i] = 5;
-	    model_data.NSigma[4] = 4.;
-	    model_data.NSigma[5] = 3.5;
- 	    for (i=6; i < number_band; i++)
-            model_data.NSigma[i] = 3.;
-    }
-
-    if (this->suppress_isolated_pixels)
-        model_data.SupIsol = True;
-
-    model_data.OnlyPositivDetect = (Bool)this->keep_positiv_sup;
-    model_data.NiterSigmaClip = this->niter_sigma_clip;
-    model_data.SizeBlockSigmaNoise = this->size_block;
-    model_data.FirstDectectScale = this->first_scale;
-    model_data.CCD_Gain = this->pas_codeur;
-    model_data.CCD_ReadOutSigma = this->sigma_gauss;
-    model_data.CCD_ReadOutMean = this->mean_gauss;
-    model_data.TypeThreshold = this->threshold;
-    model_data.U_Filter = this->undec_filter;
-    model_data.PoissonFisz = False;
-    model_data.MadEstimFromCenter = False;
-    model_data.GetEgde = False;
-
-
-    if (this->min_event_number > 0)
-        model_data.MinEventNumber = this->min_event_number;
-
-    if (this->rms_map != ""){
-       model_data.UseRmsMap = True;
-       io_read_ima_float(to_char(this->rms_map), model_data.RmsMap);
-    }
-}
-
-MRFiltering MRFilters::filtering_init(MRFiltering CFilter, Ifloat data)
-{
-    if (this->background_model_image != "") //i.e. if (use_background_model_image == True)
-    {
-       io_read_ima_float(to_char(this->background_model_image), CFilter.BackgroundData);
-       if ((CFilter.BackgroundData.nl() != data.nl()) || (CFilter.BackgroundData.nc() != data.nc()))
-           throw std::invalid_argument("Error: the background image must have the same size as the input data.");
-       CFilter.BackgroundImage = True;
-    }
-
-    CFilter.MissingData = (Bool)this->missing_data;
-    if (this->missing_data)
-    {
-        if (this->mask_file_name != "") //i.e. if (use_mask_file == True)
-        {
-            (CFilter.MaskIma).alloc(data.nl(), data.nc(), "mask");
-            for (float i = 0; i < data.nl(); i++)
-                for (float j = 0; j < data.nc(); j++)
-                    (CFilter.MaskIma)(i,j) = (data(i,j) == 0) ? 0: 1;
-        }
-        else
-        {
-            io_read_ima_float(to_char(this->mask_file_name), CFilter.MaskIma);
-            if ((CFilter.MaskIma.nl() != data.nl()) || (CFilter.MaskIma.nc() != data.nc() ))
-                throw std::invalid_argument("Error: Mask size = " + std::to_string(CFilter.MaskIma.nl()) + ", "
-                + std::to_string(CFilter.MaskIma.nc()) + " Data size = " + std::to_string(data.nl()) + ", " + std::to_string(data.nc()));
-        } 
-    }
-
-    CFilter.Max_Inpainting_Iter = this->max_inpainting_iter;
-    CFilter.Max_Iter = this->iter_max;
-    CFilter.KillLastScale = (Bool)this->kill_last_scale;
-    CFilter.Epsilon = this->epsilon;
-    CFilter.PositivIma = (Bool)this->positiv_ima;
-    CFilter.MaxIma = (Bool)this->max_ima;
-    CFilter.Verbose = (Bool)this->verbose;
-    CFilter.UseFlatField = (Bool)(this->flat_image != "");
-
-    if ((this->regul_param != 0.1) || (this->filter == FILTER_TV_CONSTRAINT)) //default value=0.1
-        CFilter.RegulParam = this->regul_param;
-    
-    if (this->flat_image != "")
-        io_read_ima_float(to_char(this->flat_image), CFilter.FlatField);
-
-    if (this->positive_recons_filter)
-        CFilter.Sup_Set = False;
-
-    return CFilter;
-}
-
-void MRFilters::write_on_prob_map(MRFiltering CFilter, Ifloat data)
-{
-    float b,i,j;
-    if(this->write_info_on_prob_map)
-        model_data.write_in_few_event(True);
-
-    mr.alloc(data.nl(), data.nc(),this->number_of_scales,model_data.type_trans(),
-                    model_data.filter_bank(), model_data.TypeNorm, model_data.nbr_undec_scale(), model_data.U_Filter);
-
-    if (model_data.TransImag)
-        model_data.im_transform(data);
-    
-    float last_scale = mr.nbr_band() - 1;
-
-    if (this->background_model_image != "")
-        data -= CFilter.BackgroundData;
-    
-    mr.transform(data);
-
-    if (this->keep_positiv_sup) {
-        for (b = 0; b < last_scale; b++)
-        for (i = 0; i < mr.size_band_nl(b); i++)
-        for (j = 0; j < mr.size_band_nc(b); j++)
-            if (mr(b,i,j) < 0) mr(b,i,j) = 0.;
-    }
-
-    // Last scale contains the max SNR through the scales
-    b = last_scale;
-    for (i=0; i< mr.size_band_nl(b); i++)
-    for (j=0; j< mr.size_band_nc(b); j++)
-        mr(b,i,j)=0;
-
-    for (b = 0; b < last_scale; b++)
-    for (i = 0; i < mr.size_band_nl(b); i++)
-    for (j = 0; j < mr.size_band_nc(b); j++)
-    {
-        double Coef = model_data.prob_signal_few_event( mr(b,i,j),  b, i, j ) ;
-        if (b < last_scale)
-        {
-            if (Coef < 1e-35)
-                Coef = (float) (sqrt((double) 2.) * fabs(dierfc((1e-35))));
-            else
-                Coef = sqrt(2.) * ABS(dierfc(Coef));
-            mr(b,i,j) = Coef;
-        }
-        if (mr(b,i,j) > mr(last_scale,i,j))
-            mr(last_scale,i,j) = mr(b,i,j);
-    }
-    mr.write(to_char(this->prob_mr_file));
-}
-
-py::array_t<float> MRFilters::Filter(py::array_t<float>& arr)
-{
-    if (this->verbose == True)
-        Info();
-
-    Ifloat data = array2image_2d(arr);
-    Ifloat Result(data.nl(), data.nc(), (char *) "Result Filtering");
-    check_scale(data.nl(), data.nc(), this->number_of_scales);
-
-
-    // noise model class initialization
-    NoiseInit(data);
-
-    // Filtering class initialization
-    MRFiltering CFilter(model_data, this->filter);
-
-    CFilter = filtering_init(CFilter, data);
-
-    //perform the filter operation
-    CFilter.filter(data, Result);
-       
-    // support image creation
-    if ((this->support_file_name != "") && (this->number_of_scales > 1))
-        model_data.write_support_mr(to_char(this->support_file_name));
-
-    if ((this->verbose) && (this->stat_noise == NOISE_GAUSSIAN)  && (ABS(this->sigma_noise) < FLOAT_EPSILON))
-         cout << "Noise standard deviation = " <<  model_data.SigmaNoise << endl;
-    
-    //write info used for computing the probability map
-    if ((this->stat_noise == NOISE_EVENT_POISSON) && (this->prob_mr_file != ""))
-        write_on_prob_map(CFilter, data);
-    return image2array_2d(Result);
-}
-void MRFilters::Info(){
-    cout << endl << endl << "PARAMETERS: " << endl << endl;
-
-    cout << "Transform = " << StringTransform(this->transform) << endl;
-    cout << "Number of scales = " << this->number_of_scales << endl;
-    cout << "Noise Type = " << StringNoise(this->stat_noise) << endl;
-    if ((this->transform == TO_MALLAT) || (this->transform == TO_UNDECIMATED_MALLAT))
-    {
-        cout << StringSBFilter(this->sb_filter) << endl;
-        //if (Norm == NORM_L2) cout << "L2 normalization" << endl;
-        if (this->transform == TO_UNDECIMATED_MALLAT)
-            cout << "Number of undecimated scales = " <<  this->number_undec << endl;
-    }
-    if (this->transform == TO_UNDECIMATED_NON_ORTHO)
-        cout << "Undec. Filter Bank = " << StringUndecFilter(this->undec_filter) << endl;
-    if (this->stat_noise == NOISE_GAUSS_POISSON)
-    {
-        cout << "Type of Noise = POISSON" << endl;
-        cout << "  Gain = " << this->pas_codeur << endl;
-        cout << "  Read-out Noise Sigma  = " << this->sigma_gauss << endl;
-        cout << "  Read-out Mean = " << this->mean_gauss << endl;
-    }
-    if ((this->stat_noise == NOISE_GAUSSIAN) && (this->sigma_noise > FLOAT_EPSILON))
-        cout << "Sigma Noise = " << this->sigma_noise << endl;
-    if (this->stat_noise ==  NOISE_EVENT_POISSON)
-        cout << "Epsilon Poisson = " <<  this->epsilon_poisson << endl;
-    if  (this->stat_noise == NOISE_SPECKLE)
-        cout << "Epsilon speckle = " <<  this->epsilon_poisson << endl;
-    if ((this->stat_noise !=  NOISE_EVENT_POISSON)
-            && (this->stat_noise != NOISE_SPECKLE))
-        cout << "N_Sigma = " << this->n_sigma << endl;
-
-    cout << "Filter = " << StringFilter(this->filter) << endl;
-    cout << "Epsilon = " << this->epsilon << endl;
-    cout << "Max_Iter = " << this->iter_max << endl;
-    if (this->max_inpainting_iter != DEFAULT_MAX_ITER_INPAINTING)
-        cout << "Max_Inpainting_Iter = " << this->max_inpainting_iter << endl;
-    if (this->positiv_ima == True) cout << "Positivity constraint" << endl;
-    else cout << "No positivity constraint" << endl;
-
-    if (this->max_ima == True) cout << "Maximum level constraint" << endl;
-    else cout << "No maximum level constraint" << endl;
-
-    if (this->keep_positiv_sup == True)
-        cout << "Only positive wavelet coefficients are detected" << endl;
-    if (this->first_scale > 0)
-        cout << "Start the detect at scale " << this->first_scale + 1 << endl;
-    if (this->kill_last_scale == True)
-        cout << "Suppress the last scale" << endl;
-
-    if (this->support_file_name != "")
-        cout << "Support Image file name : " << this->support_file_name << endl;
-}
-
-#endif
diff --git a/sparse2d/python/mr_2d1d.hpp b/sparse2d/python/mr_2d1d.hpp
deleted file mode 100644
index 20a636f4..00000000
--- a/sparse2d/python/mr_2d1d.hpp
+++ /dev/null
@@ -1,316 +0,0 @@
-#include "sparse2d/IM_Obj.h"
-#include "sparse2d/IM_IO.h"
-#include "sparse2d/MR1D_Obj.h"
-#include "sparse2d/MR_Obj.h"
-#include "sparse2d/IM3D_IO.h"
-#include "sparse2d/MR3D_Obj.h"
-#include "sparse2d/DefFunc.h"
-
-#include "numpydata.hpp"
-
-class MR2D1D {
-
-    public:
-        MR2D1D (int type_of_transform=(int)TO_MALLAT,bool normalize=false,bool verbose=false, int nb_scale_2d=5, int nb_scale_1d=4);
-
-        void alloc();
-        void Info();
-        void perform_transform (fltarray &Data);
-        fltarray write_result(int Nelem);
-
-        float & operator() (int s2, int s1, int i, int j, int k) const;
-        fltarray get_band(int s2, int s1);
-
-        py::array_t<float> Reconstruct(py::array_t<float> data);
-        py::array_t<float> Transform(py::array_t<float> Name_Cube_In);
-    
-    private:
-        int Nx, Ny, Nz;
-        int nb_scale_2d;
-        int nb_scale_1d;
-        int nbr_band_2d;
-        int nbr_band_1d;
-        type_transform  transform;
-        Bool verbose;
-        bool normalize=False;
-
-        MultiResol WT2D;
-        MR_1D WT1D;
-        fltarray *TabBand;
-        intarray tab_first_pos_band_nz;
-        intarray size_band_nx;
-        intarray size_band_ny;
-        intarray size_band_nz;
-        FilterAnaSynt fas;
-};
-
-MR2D1D::MR2D1D (int type_of_transform,bool normalize,bool verbose, int nb_scale_2d, int nb_scale_1d)
-{
-    nbr_band_2d = nbr_band_1d = 0;
-    this->verbose = (Bool)verbose;
-    this->normalize = normalize;
-    this->nb_scale_2d = nb_scale_2d;
-    this->nb_scale_1d = nb_scale_1d;
-
-
-    if ((type_of_transform > 0) && (type_of_transform <= NBR_TRANSFORM+1)) 
-        transform = (type_transform) (type_of_transform-1);
-    else
-        throw std::invalid_argument("bad type of multiresolution transform: " + std::to_string(type_of_transform));
-
-    if ((nb_scale_2d <= 1) || (nb_scale_2d > MAX_SCALE_1D)) 
-        throw std::invalid_argument("bad number of scales: "+std::to_string(nb_scale_2d));
-
-    if ((nb_scale_1d <= 0) || (nb_scale_1d > MAX_SCALE_1D))
-        throw std::invalid_argument("bad number of scales: "+std::to_string(nb_scale_1d));
-} 
-
-
-float & MR2D1D::operator() (int s2, int s1, int i, int j, int k) const
-{
-    if ( (i < 0) || (i >= size_band_nx(s2,s1)) ||
-            (j < 0) || (j >= size_band_ny(s2,s1)) ||
-        (k < 0) || (k >= size_band_nz(s2,s1)) ||
-        (s2 < 0) || (s2 >= nbr_band_2d) ||
-        (s1 < 0) || (s1 >= nbr_band_1d))
-    {
-        throw std::invalid_argument("Error: invalid number of scales");
-    }
-  
-   return TabBand[s2] (i,j,k+tab_first_pos_band_nz(s1));
-}
-
-fltarray MR2D1D::get_band(int s2, int s1)
-{
-    int Nxb = size_band_nx(s2,s1);
-    int Nyb = size_band_ny(s2,s1);
-    int Nzb = size_band_nz(s2,s1);
-    fltarray *Cube_Return = NULL;
-
-    Cube_Return = new fltarray(Nxb, Nyb, Nzb);
-    for (int i=0; i < Nxb; i++)
-    for (int j=0; j < Nyb; j++)
-    for (int k=0; k < Nzb; k++) (*Cube_Return)(i,j,k) = (*this)(s2,s1,i,j,k);
-    
-    return (*Cube_Return);
-}
-
-void MR2D1D::alloc()
-{
-    FilterAnaSynt *ptrfas = NULL;
-    if ((transform == TO_MALLAT) || (transform == TO_UNDECIMATED_MALLAT))
-    {
-        fas.Verbose = verbose;
-        fas.alloc(F_MALLAT_7_9); //sb_filter
-        ptrfas = &fas;
-    }
-    if (transform == TO_LIFTING)
-        WT2D.LiftingTrans = DEF_LIFT;
-    WT2D.Border = I_CONT;
-    WT2D.Verbose = verbose;    
-    WT2D.alloc (Ny, Nx, nb_scale_2d, transform, ptrfas, NORM_L2, -1, DEF_UNDER_FILTER);
-    nbr_band_2d = WT2D.nbr_band();
-    WT2D.ModifiedATWT = True;
-
-    WT1D.U_Filter = DEF_UNDER_FILTER;
-    WT1D.alloc (Nz, TO1_MALLAT, nb_scale_1d, ptrfas, NORM_L2, False);   
-    nbr_band_1d = WT1D.nbr_band();
-
-    TabBand = new fltarray [nbr_band_2d];
-    size_band_nx.resize(nbr_band_2d, nbr_band_1d);
-    size_band_ny.resize(nbr_band_2d, nbr_band_1d);
-    size_band_nz.resize(nbr_band_2d, nbr_band_1d);
-    tab_first_pos_band_nz.resize(nbr_band_1d);
-    tab_first_pos_band_nz(0) =0;
-
-    for (int b=0; b < nbr_band_2d; b++) 
-    {
-        TabBand[b].alloc(WT2D.size_band_nc(b), WT2D.size_band_nl(b),  WT1D.size_ima_np ());
-        for (int b1=0; b1 < nbr_band_1d; b1++)
-        {
-            size_band_nx(b,b1) = WT2D.size_band_nc(b);
-            size_band_ny(b,b1) = WT2D.size_band_nl(b);
-            size_band_nz(b,b1) = WT1D.size_scale_np(b1);
-        }
-    }
-    for (int b1=1; b1 < nbr_band_1d; b1++)
-        tab_first_pos_band_nz(b1) = tab_first_pos_band_nz(b1-1) + size_band_nz(0,b1-1);
-}
-
-void MR2D1D::perform_transform (fltarray &Data)
-{
-    Nx = Data.nx();
-    Ny = Data.ny();
-    Nz = Data.nz();
-
-    Ifloat Frame(Ny, Nx);
-    fltarray Vect(Nz);
-
-    // 2D wt transform per frame
-    for (int z = 0; z < Nz; z++)
-    {
-        for (int i = 0; i < Ny; i++)
-        for (int j = 0; j < Nx; j++)
-            Frame(i,j) = Data(j,i,z);
-        WT2D.transform(Frame);
-        for (int b = 0; b < nbr_band_2d; b++)
-        {
-            for (int i = 0; i < WT2D.size_band_nl(b); i++)
-            for (int j = 0; j < WT2D.size_band_nc(b); j++)
-                TabBand[b](j,i,z) = WT2D(b,i,j);
-        }
-    }
-
-    // 1D wt
-    if (nbr_band_1d >= 2)
-    {
-        int z =0;
-        for (int b = 0; b < nbr_band_2d; b++)
-        for (int i = 0; i < WT2D.size_band_nl(b); i++)
-        for (int j = 0; j < WT2D.size_band_nc(b); j++) 
-        {
-            for (int z = 0; z < Nz; z++)
-                Vect(z) = TabBand[b](j,i,z);
-            WT1D.transform(Vect);
-            z = 0;
-            for (int b1 = 0; b1 < nbr_band_1d; b1++)
-            for (int p = 0; p < WT1D.size_scale_np (b1); p++)
-                TabBand[b](j,i,z++) = WT1D(b1,p);
-        }
-    }
-}
-
-py::array_t<float> MR2D1D::Reconstruct(py::array_t<float> data)
-{
-    fltarray Tab = array2image_3d(data);
-
-    int ind=2;
-    for (int s=0; s <nbr_band_2d; s++) 
-    for (int s1=0; s1 < nbr_band_1d; s1++)
-    {
-       int Nxb = (int) Tab(ind++);
-       int Nyb = (int) Tab(ind++);
-       int Nzb = (int) Tab(ind++);
-       for (int k=0;  k < Nzb; k++) 
-       for (int j=0;  j < Nyb; j++)
-       for (int i=0;  i < Nxb; i++)
-            (*this)(s,s1,i,j,k) = Tab(ind++);
-    }
-
-    fltarray Data(Nx, Ny, Nz);
-    Ifloat Frame(Ny, Nx);
-    fltarray Vect(Nz);
-
-    // 1D wt 
-    if (nbr_band_1d >= 2)
-    {
-        int z = 0;
-        for (int b = 0; b < nbr_band_2d; b++)
-        for (int i = 0; i < WT2D.size_band_nl(b); i++)
-        for (int j = 0; j < WT2D.size_band_nc(b); j++) 
-        {
-            z = 0;
-            for (int b1=0; b1 < nbr_band_1d; b1++)
-            for (int p=0; p < WT1D.size_scale_np (b1); p++)
-                WT1D(b1,p) = TabBand[b](j,i,z++); 
-            Vect.init();
-            WT1D.recons(Vect);
-            for (int z=0; z < Nz; z++)
-                TabBand[b](j,i,z) = Vect(z);
-        }
-    }
-    // 2D wt 
-    for (int z=0; z < Nz; z++)
-    {
-        for (int b=0; b < nbr_band_2d; b++)
-        {
-            for (int i=0; i < WT2D.size_band_nl(b); i++)
-            for (int j=0; j < WT2D.size_band_nc(b); j++)
-                WT2D(b,i,j) = TabBand[b](j,i,z);
-        }   
-        WT2D.recons(Frame);
-        for (int i=0; i < Ny; i++)
-        for (int j=0; j < Nx; j++)
-            Data(j,i,z) = Frame(i,j);
-    }
-    return image2array_3d(Data);
-}
- 
-py::array_t<float> MR2D1D::Transform(py::array_t<float> Name_Cube_In)
-{
-    fltarray Dat = array2image_3d(Name_Cube_In);
-
-    Nx = Dat.nx();
-    Ny = Dat.ny();
-    Nz = Dat.nz();
-        
-    if (normalize)
-    {
-        double Mean = Dat.mean();
-        double Sigma = Dat.sigma();
-        for (int i = 0;i<Nx;i++)
-        for (int j = 0;j<Ny;j++)
-        for (int k = 0;k<Nz;k++)
-            Dat(i,j,k) = (Dat(i,j,k) - Mean) / Sigma;
-    }    
-
-    alloc();
-
-    perform_transform(Dat);
-            
-    if (verbose)
-        Info();
-
-    int Nelem=2;
-    for (int s=0; s < nbr_band_2d; s++)
-    for (int s1=0; s1 < nbr_band_1d; s1++) 
-        Nelem += 3 +  size_band_nx(s,s1)*size_band_ny(s,s1)*size_band_nz(s,s1);
-    fltarray Data = write_result(Nelem);
-
-    py::list result;
-    float *buff = Data.buffer();
-    for (int i=0; i<Nelem; i++) {
-        result.append(buff[i]);
-    }
-    return result;
-}
-
-fltarray MR2D1D::write_result(int Nelem)
-{
-    fltarray Data(Nelem);
-    int ind=2;
-    Data(0) = nbr_band_2d;
-    Data(1) = nbr_band_1d;
-    
-    // Ifloat Band;
-    for (int s=0; s < nbr_band_2d; s++)
-    for (int s1=0; s1 < nbr_band_1d; s1++) 
-    {
-        Data(ind++) = size_band_nx(s,s1);
-        Data(ind++) = size_band_ny(s,s1);
-        Data(ind++) = size_band_nz(s,s1);
-        
-        for (int k=0;  k < size_band_nz(s,s1); k++) 
-        for (int j=0;  j < size_band_ny(s,s1); j++) 
-        for (int i=0;  i < size_band_nx(s,s1); i++)
-            Data(ind++) = (*this)(s,s1,i,j,k);
-    }
-    return Data;
-}
-
-void MR2D1D::Info()
-{
-    cout << "Transform = " << StringTransform((type_transform) transform) << endl;
-    cout << "nb_scale_2d = " << this->nb_scale_2d<< endl;
-    cout << "NbrScale1d = " << this->nb_scale_1d<< endl;
-    cout << "Nx = " << Nx << " Ny = " << Ny << " Nz = " << Nz << endl;
-    cout << endl;
-    for (int s2 = 0; s2 < nbr_band_2d; s2++)
-        for (int s1 = 0; s1 < nbr_band_1d; s1++)
-        {
-            cout << "  Band " << s2 << ", " << s1 << ": " << " Nx = " << size_band_nx(s2,s1) << ", Ny = " << size_band_ny(s2,s1) <<  ", Nz = " << size_band_nz(s2,s1) << endl;
-            fltarray Band;
-            Band = get_band(s2, s1);
-            cout << "  Sigma = " << Band.sigma() << " Min = " << Band.min() << " Max = " << Band.max() << endl;
-        }
-}
diff --git a/sparse2d/python/numpydata.hpp b/sparse2d/python/numpydata.hpp
deleted file mode 100644
index 20603993..00000000
--- a/sparse2d/python/numpydata.hpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*##########################################################################
-pySAP - Copyright (C) CEA, 2017 - 2018
-Distributed under the terms of the CeCILL-B license, as published by
-the CEA-CNRS-INRIA. Refer to the LICENSE file or to
-http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
-for details.
-##########################################################################*/
-
-#ifndef NUMPYDATA_H_
-#define NUMPYDATA_H_
-
-#include <pybind11/pybind11.h>
-#include <pybind11/stl.h>
-#include <pybind11/numpy.h>
-#include <vector>
-#include <sparse2d/IM_Obj.h>
-#include <sparse2d/IM_IO.h>
-#include <iostream>
-
-namespace py = pybind11;
-
-// Helper function for fast image to array conversion
-py::array_t<float> image2array_2d(const Ifloat &image){
-
-  auto array = py::array_t<float>(image.n_elem());
-  auto buffer = array.request();
-  float *pointer = (float *) buffer.ptr;
-
-  for (int i=0; i<image.nl(); i++) {
-    for (int j=0; j<image.nc(); j++) {
-      pointer[j + i * image.nc()] = image(i, j);
-    }
-  }
-
-  array.resize({image.nl(), image.nc()});
-
-  return array;
-}
-
-// Helper function for fast arrat to image conversion
-Ifloat array2image_2d(py::array_t<float> &array){
-
-  if (array.ndim() != 2)
-    throw std::runtime_error("Input should be 2-D NumPy array");
-
-  auto buffer = array.request();
-  float *pointer = (float *) buffer.ptr;
-
-  Ifloat image(array.shape(0), array.shape(1));
-
-  for (int i=0; i<array.shape(0); i++) {
-    for (int j=0; j<array.shape(1); j++) {
-      image(i, j) = pointer[j + i * array.shape(1)];
-    }
-  }
-
-  return image;
-}
-
-// Helper function for fast image to 3D array conversion
-py::array_t<float> image2array_3d(const fltarray &image){
-
-  auto array = py::array_t<float>(image.n_elem());
-  auto buffer = array.request();
-  float *pointer = (float *) buffer.ptr;
-
-  for (int i=0; i<image.nx(); i++) {
-    for (int j=0; j<image.ny(); j++) {
-      for(int k=0; k<image.nz(); k++){
-        pointer[i + image.nx() * (j + k * image.ny())] = image(i, j, k);
-      }
-    }
-  }
-
-  array.resize({image.nz(), image.ny(), image.nx()});
-
-  return array;
-}
-
-// Helper function for fast 3D arrat to image conversion
-fltarray array2image_3d(py::array_t<float> &array) {
-
-  if (array.ndim() == 3)
-  {
-    auto buffer = array.request();
-    float *pointer = (float *) buffer.ptr;
-
-    fltarray image(array.shape(2), array.shape(1), array.shape(0));
-
-    for (int i=0; i<array.shape(2); i++) {
-      for (int j=0; j<array.shape(1); j++) {
-        for (int k=0; k<array.shape(0); k++){
-          image(i, j, k) = pointer[i + array.shape(2) * (j + k * array.shape(1))];
-        }
-      }
-    }
-    return image;
-  }
-  else if (array.ndim() == 1)
-  {
-      fltarray image(len(array));
-
-      auto buffer = array.request();
-      float *pointer = (float *) buffer.ptr;
-
-      for (int i = 0; i < len(array); ++i)
-          image(i) = pointer[i];
-      return image;
-  }
-  else
-    throw std::runtime_error("Input should be 3-D NumPy array");
-}
-
-#endif
diff --git a/sparse2d/python/pysparse.cpp b/sparse2d/python/pysparse.cpp
deleted file mode 100644
index 4228948f..00000000
--- a/sparse2d/python/pysparse.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/*##########################################################################
-pySAP - Copyright (C) CEA, 2017 - 2018
-Distributed under the terms of the CeCILL-B license, as published by
-the CEA-CNRS-INRIA. Refer to the LICENSE file or to
-http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
-for details.
-##########################################################################*/
-
-// Includes
-#include <pybind11/pybind11.h>
-#include <pybind11/stl.h>
-#include <pybind11/numpy.h>
-
-#include "transform.hpp"
-#include "transform_3D.hpp"
-#include "filter.hpp"
-#include "deconvolve.hpp"
-#include "mr_2d1d.hpp"
-#include "starlet.hpp"
-
-// Defines a python module which will be named "pysparse"
-PYBIND11_MODULE(pysparse, module)
-{
-  module.doc() = "Python bindings for Sparse2D";
-  module.attr("__version__") = "0.1.0";
-
-  py::class_<MRTransform>(module, "MRTransform")
-    .def(py::init< int, int, int, int, int, bool, int, int, int, int >(),
-        py::arg("type_of_multiresolution_transform"),
-        py::arg("type_of_lifting_transform")=(int)(3),
-        py::arg("number_of_scales")=(int)(4),
-        py::arg("iter")=(int)(3),
-        py::arg("type_of_filters")=(int)(1),
-        py::arg("use_l2_norm")=(bool)(false),
-        py::arg("type_of_non_orthog_filters")=(int)(2),
-        py::arg("bord")=(int)(0),
-        py::arg("nb_procs")=(int)(0),
-        py::arg("verbose")=(int)(0)
-      )
-    .def("info", &MRTransform::Info)
-    .def("transform", &MRTransform::Transform, py::arg("arr"), py::arg("save")=(bool)(0))
-    .def("reconstruct", &MRTransform::Reconstruct, py::arg("mr_data"))
-    .def_property("opath", &MRTransform::get_opath, &MRTransform::set_opath);
-
-  py::class_<MRTransform3D>(module, "MRTransform3D")
-    .def(py::init< int, int, int, int, int, bool, int, int >(),
-        py::arg("type_of_multiresolution_transform"),
-        py::arg("type_of_lifting_transform")=(int)(3),
-        py::arg("number_of_scales")=(int)(4),
-        py::arg("iter")=(int)(3),
-        py::arg("type_of_filters")=(int)(1),
-        py::arg("use_l2_norm")=(bool)(false),
-        py::arg("nb_procs")=(int)(0),
-        py::arg("verbose")=(int)(0)
-      )
-    .def("info", &MRTransform3D::Info)
-    .def("transform", &MRTransform3D::Transform, py::arg("arr"), py::arg("save")=(bool)(0))
-    .def("reconstruct", &MRTransform3D::Reconstruct, py::arg("mr_data"))
-    .def_property("opath", &MRTransform3D::get_opath, &MRTransform3D::set_opath);
-
-  py::class_<MRFilters>(module, "MRFilters")
-    .def(py::init
-    < int, int, int, int, int, int, int, float, double, double, double, std::string, float, std::string, std::string,
-    bool, bool, bool, double, int, int, int, std::string, std::string, int, std::string, 
-    bool, bool, bool, int, float, float, float, bool ,bool, bool, bool, bool, std::vector<float>&>(),
-        py::arg("type_of_filtering")=(int)(1),
-        py::arg("coef_detection_method")=(int)(1),
-        py::arg("type_of_multiresolution_transform")=(int)(2),
-        py::arg("type_of_filters")=(int)(1),
-        py::arg("type_of_non_orthog_filters")=(int)(2),
-        py::arg("type_of_noise")=(int)(1),
-        py::arg("number_of_scales")=(int)(DEFAULT_NBR_SCALE),
-        py::arg("regul_param")=(float)(0.1),
-        py::arg("epsilon")=(double)(DEFAULT_EPSILON_FILTERING),
-        py::arg("iter_max")=(double)(DEFAULT_MAX_ITER_FILTER),
-        py::arg("max_inpainting_iter")=(double)(DEFAULT_MAX_ITER_INPAINTING),
-        py::arg("support_file_name")=(std::string)(""),
-        py::arg("sigma_noise")=(float)(0.),
-        py::arg("flat_image")=(std::string)(""),
-        py::arg("rms_map")=(std::string)(""),
-        py::arg("missing_data")=(bool)(false),
-        py::arg("keep_positiv_sup")=(bool)(false),
-        py::arg("write_info_on_prob_map")=(bool)(false),
-        py::arg("epsilon_poisson")=(double)(1.00e-03),
-        py::arg("size_block")=(int)(7),
-        py::arg("niter_sigma_clip")=(int)(1),
-        py::arg("first_scale")=(int)(1),
-        py::arg("mask_file_name")=(std::string)(""),
-        py::arg("prob_mr_file")=(std::string)(""),
-        py::arg("min_event_number")=(int)(0),
-        py::arg("background_model_image")=(std::string)(""),
-        py::arg("positive_recons_filter") =(bool)(false),
-        py::arg("suppress_isolated_pixels")=(bool)(false),
-        py::arg("verbose")=(bool)(false),
-        py::arg("number_undec")=(int)(-1),
-        py::arg("pas_codeur")=(float)(1),
-        py::arg("sigma_gauss")=(float)(0.),
-        py::arg("mean_gauss")=(float)(0.),
-        py::arg("old_poisson")=(bool)(false),
-        py::arg("positiv_ima")=(bool)(DEF_POSITIV_CONSTRAINT),
-        py::arg("max_ima")=(bool)(DEF_MAX_CONSTRAINT),
-        py::arg("kill_last_scale")=(bool)(false),
-        py::arg("write_threshold")=(bool)(false),
-        py::arg("tab_n_sigma")=(std::vector<float>&)(v)
-    )
-    .def("info", &MRFilters::Info)
-    .def("filter", &MRFilters::Filter, py::arg("arr"));
-
-  py::class_<MRDeconvolve>(module, "MRDeconvolve")
-    .def(py::init<int, int, int, int, float, int, int, float, int, float, bool,
-                bool, bool, float, float, float, std::string, std::string,
-                std::string, bool, bool, bool, bool, float, float, float>(),
-        py::arg("type_of_deconvolution")=(int)(3),
-        py::arg("type_of_multiresolution_transform")=(int)(2),
-        py::arg("type_of_filters")=(int)(1),
-        py::arg("number_of_undecimated_scales")=(int)(-1),
-        py::arg("sigma_noise")=(float)(0.),
-        py::arg("type_of_noise")=(int)(1),
-        py::arg("number_of_scales")=(int)(DEFAULT_NBR_SCALE),
-        py::arg("nsigma")=(float)(DEFAULT_N_SIGMA),
-        py::arg("number_of_iterations")=(int)(DEFAULT_MAX_ITER_DECONV),
-        py::arg("epsilon")=(float)(DEFAULT_EPSILON_DECONV),
-        py::arg("psf_max_shift")=(bool)(true),
-        py::arg("verbose")=(bool)(false),
-        py::arg("optimization")=(bool)(false),
-        py::arg("fwhm_param")=(float)(0.),
-        py::arg("convergence_param")=(float)(1.),
-        py::arg("regul_param")=(float)(0.),
-        py::arg("first_guess")=(std::string)(""),
-        py::arg("icf_filename")=(std::string)(""),
-        py::arg("rms_map")=(std::string)(""),
-        py::arg("kill_last_scale")=(bool)(false),
-        py::arg("positive_constraint")=(bool)(true),
-        py::arg("keep_positiv_sup")=(bool)(false),
-        py::arg("sup_isol")=(bool)(false),
-        py::arg("pas_codeur")=(float)(1),
-        py::arg("sigma_gauss")=(float)(0),
-        py::arg("mean_gauss")=(float)(0)
-      )
-      .def("info", &MRDeconvolve::Info)
-      .def("deconvolve", &MRDeconvolve::Deconvolve, py::arg("arr"), py::arg("psf"));
-
-      py::class_<MR2D1D>(module, "MR2D1D")
-    .def(py::init<int, bool, bool, int, int >(),
-        py::arg("type_of_transform")=(int)(14),
-        py::arg("normalize")=(bool)(False),
-        py::arg("verbose")=(bool)(False),
-        py::arg("NbrScale2d")=(int)(5),
-        py::arg("Nbr_Plan")=(int)(4)
-      )
-    .def("transform", &MR2D1D::Transform, py::arg("Name_Cube_in"))
-    .def("reconstruct", &MR2D1D::Reconstruct, py::arg("data"));
-    
-      py::class_<MRStarlet>(module, "MRStarlet")
-    .def(py::init<int, bool, int, int >(),
-        py::arg("bord")=(int)(0),
-        py::arg("gen2")=(bool)(False),
-        py::arg("nb_procs")=(int)(0),
-        py::arg("verbose")=(int)(0)
-     )
-    .def("transform", &MRStarlet::transform, py::arg("arr"), py::arg("ns"))
-    .def("recons", &MRStarlet::recons, py::arg("mr_data"),py::arg("adjoint")=(bool)(0))
-    .def("info", &MRStarlet::info);
-}
diff --git a/sparse2d/python/starlet.hpp b/sparse2d/python/starlet.hpp
deleted file mode 100644
index 586f38a8..00000000
--- a/sparse2d/python/starlet.hpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*##########################################################################
-pySAP - Copyright (C) CEA, 2017 - 2018
-Distributed under the terms of the CeCILL-B license, as published by
-the CEA-CNRS-INRIA. Refer to the LICENSE file or to
-http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
-for details.
-##########################################################################*/
-
-#ifndef STARLET_H_
-#define STARLET_H_
-
-// Includes
-#include <iostream>
-#include <string>
-#include <sstream>
-#include <sparse2d/IM_Obj.h>
-#include <sparse2d/IM_IO.h>
-#include <sparse2d/MR_Obj.h>
-#include <sparse2d/IM_Prob.h>
-#include "numpydata.hpp"
-
-
-class MRStarlet {
-
-public:
-    // Constructor
-    MRStarlet(
-        int bord=0,
-        bool gen2=false,
-        int nb_procs=0,
-        int verbose=0);
-
-    // Destructor
-    ~MRStarlet();
- 
-    // Information method
-    void info();
-    
-    py::list transform(py::array_t<float>& arr, int Ns=4);
-
-    // Reconstruction method
-    py::array_t<float> recons(py::list mr_data, bool adjoint=false);
-
-    // Getter/setter functions for the input/output image path
-    void set_opath(std::string path) {this->m_opath = path;}
-    string get_opath() const {return m_opath;}
-
-private:
-    ATROUS_2D_WT mr;
-    bool mr_initialized;
-    std::string m_opath;
-    int nb_procs;
-    int number_of_scales;
-    Ifloat *TabTrans;
-    bool gen2;
-    type_border bord;
-    int verbose;
-};
-
-// Constructor
-MRStarlet::MRStarlet(
-        int bord,
-        bool gen2,
-        int nb_procs,
-        int verbose){
-    // Define instance attributes
-    switch (bord)
-    {
-      case 0: this->bord = I_ZERO; break;
-      case 1: this->bord = I_CONT; break;
-      case 2: this->bord = I_MIRROR; break;
-      case 3: this->bord = I_PERIOD; break;
-      default:
-         throw std::invalid_argument("Error: bad parameter bord.");
-    }
-    this->bord = I_CONT;
-    this->number_of_scales = 0;
-    this->verbose = verbose;
-    this->mr_initialized = false;
-    this->gen2=gen2;
-    this->TabTrans=NULL;
-    // The maximum number of threads returned by omp_get_max_threads()
-    // (which is the default number of threads used by OMP in parallel
-    // regions) can sometimes be far below the number of CPUs.
-    // It is then required to set it in relation to the real number of CPUs
-    // (the -1 is used to live one thread to the main process which ensures
-    // better and more constant performances). - Fabrice Poupon 2013/03/09
-    #ifdef _OPENMP
-        if (nb_procs <= 0)
-            this->nb_procs = omp_get_num_procs() - 1;
-        else
-            this->nb_procs = nb_procs;
-        omp_set_num_threads(this->nb_procs);
-    #endif
-
-}
-
-// Destructor
-MRStarlet::~MRStarlet(){
-}
-
-
-void MRStarlet::info(){
-    // Information message
-    cout << "---------" << endl;
-    cout << "Information" << endl;
-    cout << "Runtime parameters:" << endl;
-    cout << "  Number of procs: " << this->nb_procs << endl;
-    cout << "  MR border type: " << this->bord << endl;
-    cout << "  Number of scales: " << this->number_of_scales << endl;
-    if (gen2) cout << "  Second Starlet generation" << endl;
-    else  cout << "  First Starlet generation" << endl;
-    cout << "---------" << endl;
-}
-
-// Transform method
-py::list MRStarlet::transform(py::array_t<float>& arr, int Ns){
-    // Load the input image
-    /*if (this->verbose > 0)
-        cout << "Loading input image '" << this->m_ipath << "'..." << endl;
-    Ifloat data;
-    io_read_ima_float((char *)this->m_ipath.c_str(), data);
-    if (this->verbose > 0)
-        cout << "Done." << endl;*/
-
-    // Create the transformation
-    Ifloat data = array2image_2d(arr);
-
-    if (!this->mr_initialized) {
-        //MultiResol mr;
-        mr.alloc (TabTrans, data.nl(), data.nc(), Ns);
-        mr.Bord = this->bord;
-        this->number_of_scales = Ns;
-        mr.ModifiedAWT = (Bool)this->gen2;
-        this->mr_initialized = true;
-    }
-    else
-    {
-        if ((this->number_of_scales != Ns) || (data.nl() != TabTrans[0].nl()) || (data.nc() != TabTrans[0].nc()))
-        {
-            delete [] TabTrans;
-            mr.alloc (TabTrans, data.nl(), data.nc(), Ns);
-            mr.Bord = this->bord;
-            this->number_of_scales = Ns;
-            mr.ModifiedAWT = (Bool)this->gen2;
-        }
-    }
-
-    // Perform the transformation
-    if (this->verbose > 0) {
-        cout << "Starting transformation" << endl;
-        cout << "Runtime parameters:" << endl;
-        if (mr.ModifiedAWT == False) cout << "  First Starlet generation" << endl;
-        else cout << "  Second Starlet generation" << endl;
-        cout << "  Number of bands: " << this->number_of_scales << endl;
-        cout << "  Data dimension: " << arr.ndim() << endl;
-        cout << "  Array shape: " << arr.shape(0) << ", " << arr.shape(1) << endl;
-    }
-
-    mr.transform(data, TabTrans, Ns, this->nb_procs);
-
-    // Return the generated bands data
-            py::list mr_data;
-    for (int s=0; s< Ns; s++) {
-        mr_data.append(image2array_2d(TabTrans[s]));
-    }
-
-    // Format the result
-    // py::list mr_result;
-    // mr_result.append(mr_data);
-    // return mr_result;
-    return mr_data;
-}
-            
-// Reconstruction method
-py::array_t<float> MRStarlet::recons(py::list mr_data, bool adjoint){
-    // Welcome message
-    int nx = py::array(mr_data[0]).shape(0);
-    int ny = py::array(mr_data[0]).shape(1);
-    int ns = py::len(mr_data);
-    if (this->verbose > 0) {
-        cout << "Starting Reconstruction" << endl;
-        cout << "Runtime parameters:" << endl;
-        cout << "  Number of bands: " << py::len(mr_data) << endl;
-        cout << "  Image size : " << nx << " " << ny  << endl;
-    }
-
-    if (!this->mr_initialized) {
-        //MultiResol mr;
-        mr.alloc (TabTrans, nx, ny, ns);
-        mr.Bord = this->bord;
-        this->number_of_scales = ns;
-        mr.ModifiedAWT = (Bool)this->gen2;
-        this->mr_initialized = true;
-    }
-    
-    // Update transformation
-    for (int s=0; s<py::len(mr_data); s++) {
-        py::array_t<float> band_array = py::array(mr_data[s]);
-        Ifloat band_data = array2image_2d(band_array);
-        // cout << "Size of inserted band ";
-        // cout << "nb_e:"<< band_data.n_elem() << "/ndim:" << band_data.naxis()\
-        // << "/nx:" << band_data.nx() << "/ny:"  << band_data.ny() <<  endl;
-        TabTrans[s] = band_data;
-    }
-
-    // Start the reconstruction
-    Ifloat data(TabTrans[0].nl(), TabTrans[0].nc(), "Reconstruct");
-    if (adjoint) mr.adjoint_recons(TabTrans, data, this->number_of_scales, this->nb_procs);
-    else mr.recons(TabTrans, data, this->number_of_scales, this->nb_procs);
-    return image2array_2d(data);
-}
-
-#endif
diff --git a/sparse2d/python/transform.hpp b/sparse2d/python/transform.hpp
deleted file mode 100644
index fa1549a5..00000000
--- a/sparse2d/python/transform.hpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/*##########################################################################
-pySAP - Copyright (C) CEA, 2017 - 2018
-Distributed under the terms of the CeCILL-B license, as published by
-the CEA-CNRS-INRIA. Refer to the LICENSE file or to
-http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
-for details.
-##########################################################################*/
-
-#ifndef TRANSFORM_H_
-#define TRANSFORM_H_
-
-// Includes
-#include <iostream>
-#include <string>
-#include <sstream>
-#include <sparse2d/IM_Obj.h>
-#include <sparse2d/IM_IO.h>
-#include <sparse2d/MR_Obj.h>
-#include <sparse2d/IM_Prob.h>
-#include "numpydata.hpp"
-
-
-class MRTransform {
-
-public:
-    // Constructor
-    MRTransform(
-        int type_of_multiresolution_transform,
-        int type_of_lifting_transform=3,
-        int number_of_scales=4,
-        int iter=3,
-        int type_of_filters=1,
-        bool use_l2_norm=false,
-        int type_of_non_orthog_filters=2,
-        int bord=0,
-        int nb_procs=0,
-        int verbose=0);
-
-    // Destructor
-    ~MRTransform();
-
-    // Save transformation method
-    void Save(MultiResol &mr);
-
-    // Update transformation method
-    MultiResol Update(int nl, int nc);
-
-    // Information method
-    void Info();
-
-    // Transform method
-    py::list Transform(py::array_t<float>& arr, bool save=false);
-
-    // Reconstruction method
-    py::array_t<float> Reconstruct(py::list mr_data);
-
-    // Getter/setter functions for the input/output image path
-    void set_opath(std::string path) {this->m_opath = path;}
-    string get_opath() const {return m_opath;}
-
-private:
-    MultiResol mr;
-    FilterAnaSynt fas;
-    FilterAnaSynt *ptrfas = NULL;
-    bool mr_initialized;
-    std::string m_opath;
-    int type_of_multiresolution_transform;
-    int type_of_lifting_transform;
-    int number_of_scales;
-    int iter;
-    int type_of_filters;
-    bool use_l2_norm;
-    int type_of_non_orthog_filters;
-    int nb_procs;
-    int verbose;
-    type_transform mr_transform = DEFAULT_TRANSFORM;
-    type_lift lift_transform = DEF_LIFT;
-    type_sb_filter filter = F_MALLAT_7_9;
-    sb_type_norm norm = NORM_L1;
-    type_undec_filter no_filter = DEF_UNDER_FILTER;
-    type_border bord;
-    int nb_of_undecimated_scales;
-};
-
-// Constructor
-MRTransform::MRTransform(
-        int type_of_multiresolution_transform,
-        int type_of_lifting_transform,
-        int number_of_scales,
-        int iter,
-        int type_of_filters,
-        bool use_l2_norm,
-        int type_of_non_orthog_filters,
-        int bord,
-        int nb_procs,
-        int verbose){
-    // Define instance attributes
-    switch (bord)
-    {
-      case 0: this->bord = I_ZERO; break;
-      case 1: this->bord = I_CONT; break;
-      case 2: this->bord = I_MIRROR; break;
-      case 3: this->bord = I_PERIOD; break;
-      default:
-         throw std::invalid_argument("Error: bad parameter bord.");
-    }
-    this->bord = I_CONT;
-    this->type_of_multiresolution_transform = type_of_multiresolution_transform;
-    this->type_of_lifting_transform = type_of_lifting_transform;
-    this->number_of_scales = number_of_scales;
-    this->iter = iter;
-    this->type_of_filters = type_of_filters;
-    this->use_l2_norm = use_l2_norm;
-    this->type_of_non_orthog_filters = type_of_non_orthog_filters;
-    this->verbose = verbose;
-    this->nb_of_undecimated_scales = -1;
-    this->mr_initialized = false;
-
-    // The maximum number of threads returned by omp_get_max_threads()
-    // (which is the default number of threads used by OMP in parallel
-    // regions) can sometimes be far below the number of CPUs.
-    // It is then required to set it in relation to the real number of CPUs
-    // (the -1 is used to live one thread to the main process which ensures
-    // better and more constant performances). - Fabrice Poupon 2013/03/09
-    #ifdef _OPENMP
-        if (nb_procs <= 0)
-            this->nb_procs = omp_get_num_procs() - 1;
-        else
-            this->nb_procs = nb_procs;
-        omp_set_num_threads(this->nb_procs);
-    #endif
-
-    // Load the mr transform
-    if ((this->type_of_multiresolution_transform > 0) && (this->type_of_multiresolution_transform <= NBR_TOT_TRANSFORM+1))
-        this->mr_transform = type_transform(this->type_of_multiresolution_transform - 1);
-    else
-        throw std::invalid_argument("Invalid MR transform number.");
-
-    // Load the lifting transform
-    if ((this->type_of_lifting_transform > 0) && (this->type_of_lifting_transform <= NBR_LIFT))
-        this->lift_transform = type_lift(this->type_of_lifting_transform);
-    else
-        throw std::invalid_argument("Invalid lifting transform number.");
-
-    // Check the number of scales
-    if ((this->number_of_scales <= 1) || (this->number_of_scales > MAX_SCALE))
-        throw std::invalid_argument("Bad number of scales ]1; MAX_SCALE].");
-
-    // Check the number of iterations
-    if ((this->iter <= 1) || (this->iter > 20))
-        throw std::invalid_argument("Bad number of iteration ]1; 20].");
-
-    // Load the filter
-    if (this->type_of_filters != 1){
-        std::stringstream strs;
-        strs << type_of_filters;
-        this->filter = get_filter_bank((char *)strs.str().c_str());
-    }
-
-    // Change the norm
-    if (this->use_l2_norm)
-        this->norm = NORM_L2;
-
-    // Load the non orthogonal filter
-    if ((this->type_of_non_orthog_filters > 0) && (this->type_of_non_orthog_filters <= NBR_UNDEC_FILTER))
-        this->no_filter = type_undec_filter(this->type_of_non_orthog_filters - 1);
-
-    // Check input parameters
-	if ((this->mr_transform != TO_LIFTING) && (this->lift_transform != DEF_LIFT))
-        throw std::invalid_argument("-l option is only available with lifting transform.");
-	if ((this->mr_transform != TO_UNDECIMATED_MALLAT) && (this->mr_transform != TO_MALLAT) &&
-            (this->use_l2_norm))
-	    throw std::invalid_argument("-T and -L options are only valid with Mallat transform.");
-}
-
-// Destructor
-MRTransform::~MRTransform(){
-}
-
-// Save transformation method
-void MRTransform::Save(MultiResol &mr){
-
-    // Welcome message
-    if (this->verbose > 0)
-        cout << "  Output path: " << this->m_opath << endl;
-
-    // Check inputs
-    if (this->m_opath == "")
-        throw std::invalid_argument(
-            "Please specify an output image path in 'opath'.");
-
-    // Write the results
-    mr.write((char *)this->m_opath.c_str());
-}
-
-void MRTransform::Info(){
-    // Information message
-    cout << "---------" << endl;
-    cout << "Information" << endl;
-    cout << "Runtime parameters:" << endl;
-    cout << "  Number of procs: " << this->nb_procs << endl;
-    cout << "  MR transform ID: " << this->type_of_multiresolution_transform << endl;
-    cout << "  MR transform name: " << StringTransform(this->mr_transform) << endl;
-    cout << "  MR border type: " << this->bord << endl;
-    if ((this->mr_transform == TO_MALLAT) || (this->mr_transform == TO_UNDECIMATED_MALLAT)) {
-        cout << "  Filter ID: " << this->type_of_filters << endl;
-        cout << "  Filter name: " << StringSBFilter(this->filter) << endl;
-        if (this->use_l2_norm)
-            cout << "   Use L2-norm." << endl;
-    }
-    if (this->mr_transform == TO_LIFTING) {
-        cout << "  Lifting transform ID: " << this->type_of_lifting_transform << endl;
-        cout << "  Lifting transform name: " << StringLSTransform(this->lift_transform) << endl;
-    }
-    cout << "  Number of scales: " << this->number_of_scales << endl;
-    if (this->mr_transform == TO_UNDECIMATED_MALLAT)
-        cout << "  Number of undecimated scales: " <<  this->nb_of_undecimated_scales << endl;
-    if (this->mr_transform == TO_UNDECIMATED_NON_ORTHO) {
-        cout << "Undecimated non othogonal filter ID: " << this->type_of_non_orthog_filters << endl;
-        cout << "Undecimated non othogonal filter name: " << StringUndecFilter(this->no_filter) << endl;
-    }
-    if (this->verbose > 1)
-        mr.print_info();
-    cout << "---------" << endl;
-}
-
-// Transform method
-py::list MRTransform::Transform(py::array_t<float>& arr, bool save){
-    // Load the input image
-    /*if (this->verbose > 0)
-        cout << "Loading input image '" << this->m_ipath << "'..." << endl;
-    Ifloat data;
-    io_read_ima_float((char *)this->m_ipath.c_str(), data);
-    if (this->verbose > 0)
-        cout << "Done." << endl;*/
-
-    // Create the transformation
-    Ifloat data = array2image_2d(arr);
-    if (!this->mr_initialized) {
-        //MultiResol mr;
-        //FilterAnaSynt fas;
-        //FilterAnaSynt *ptrfas = NULL;
-        if ((this->mr_transform == TO_MALLAT) || (this->mr_transform == TO_UNDECIMATED_MALLAT)) {
-            fas.Verbose = (Bool)this->verbose;
-            fas.alloc(this->filter);
-            ptrfas = &fas;
-        }
-        mr.alloc(data.nl(), data.nc(), this->number_of_scales,
-                 this->mr_transform, ptrfas, this->norm,
-                 this->nb_of_undecimated_scales, this->no_filter);
-        if (this->mr_transform == TO_LIFTING)
-            mr.LiftingTrans = this->lift_transform;
-        mr.Border = this->bord;
-        mr.Verbose = (Bool)this->verbose;
-        this->mr_initialized = true;
-    }
-
-    // Perform the transformation
-    if (this->verbose > 0) {
-        cout << "Starting transformation" << endl;
-        cout << "Runtime parameters:" << endl;
-        cout << "  Number of bands: " << mr.nbr_band() << endl;
-        cout << "  Data dimension: " << arr.ndim() << endl;
-        cout << "  Array shape: " << arr.shape(0) << ", " << arr.shape(1) << endl;
-        cout << "  Save transform: " << save << endl;
-    }
-    mr.transform(data);
-
-    // Correct the reconstruction error
-    if ((this->iter > 1) && (mr.Set_Transform == TRANSF_PYR))
-        mr_correct_pyr(data, mr, this->iter);
-
-    // Save transform if requested
-    if (save)
-        Save(mr);
-
-    // Return the generated bands data
-    py::list mr_data;
-    for (int s=0; s<mr.nbr_band(); s++) {
-        mr_data.append(image2array_2d(mr.band(s)));
-    }
-
-    // Get the number of bands for each scale
-    py::list mr_scale;
-    int nb_bands_count = 0;
-    for (int s=0; s<mr.nbr_scale(); s++) {
-        nb_bands_count += mr.nbr_band_per_resol(s);
-        mr_scale.append(mr.nbr_band_per_resol(s));
-    }
-    if (nb_bands_count != mr.nbr_band()) {
-        mr_scale[py::len(mr_scale) - 1] = 1;
-    }
-
-    // Format the result
-    py::list mr_result;
-    mr_result.append(mr_data);
-    mr_result.append(mr_scale);
-
-    return mr_result;
-}
-
-// Reconstruction method
-py::array_t<float> MRTransform::Reconstruct(py::list mr_data){
-    // Welcome message
-    if (this->verbose > 0) {
-        cout << "Starting Reconstruction" << endl;
-        cout << "Runtime parameters:" << endl;
-        cout << "  Number of bands: " << py::len(mr_data) << endl;
-    }
-
-    // Update transformation
-    for (int s=0; s<py::len(mr_data); s++) {
-        py::array_t<float> band_array = py::array(mr_data[s]);
-        Ifloat band_data = array2image_2d(band_array);
-        // cout << "Size of inserted band ";
-        // cout << "nb_e:"<< band_data.n_elem() << "/ndim:" << band_data.naxis()\
-        // << "/nx:" << band_data.nx() << "/ny:"  << band_data.ny() <<  endl;
-
-        mr.insert_band(band_data, s);
-    }
-
-    // Start the reconstruction
-    Ifloat data(mr.size_ima_nl(), mr.size_ima_nc(), "Reconstruct");
-    mr.recons(data);
-
-    return image2array_2d(data);
-}
-
-#endif
diff --git a/sparse2d/python/transform_3D.hpp b/sparse2d/python/transform_3D.hpp
deleted file mode 100644
index 6544cceb..00000000
--- a/sparse2d/python/transform_3D.hpp
+++ /dev/null
@@ -1,317 +0,0 @@
-/*##########################################################################
-pySAP - Copyright (C) CEA, 2017 - 2018
-Distributed under the terms of the CeCILL-B license, as published by
-the CEA-CNRS-INRIA. Refer to the LICENSE file or to
-http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
-for details.
-##########################################################################*/
-
-#ifndef TRANSFORM_3D_H_
-#define TRANSFORM_3D_H_
-
-/*Availables transforms:
-1: Mallat 3D
-2: Lifting
-3: A trous*/
-
-// Includes
-#include <iostream>
-#include <string>
-#include <sstream>
-#include <typeinfo>
-#include <sparse2d/IM_Obj.h>
-#include <sparse2d/IM_IO.h>
-#include <sparse2d/IM3D_IO.h>
-#include <sparse2d/MR3D_Obj.h>
-#include <sparse2d/MR_Obj.h>
-#include <sparse2d/IM_Prob.h>
-#include "numpydata.hpp"
-
-
-
-#define ASSERT_THROW(a,msg) if (!(a)) throw std::runtime_error(msg);
-
-class MRTransform3D {
-
-public:
-    // Constructor
-    MRTransform3D(
-        int type_of_multiresolution_transform,
-        int type_of_lifting_transform=3,
-        int number_of_scales=4,
-        int iter=3,
-        int type_of_filters=1,
-        bool use_l2_norm=false,
-        int nb_procs=0,
-        int verbose=0);
-
-    // Destructor
-    ~MRTransform3D();
-
-    // Save transformation method
-    void Save(MR_3D &mr);
-
-    // Information method
-    void Info();
-
-    // Transform method
-    py::list Transform(py::array_t<float>& arr, bool save=false);
-
-    // Reconstruction method
-    py::array_t<float> Reconstruct(py::list mr_data);
-
-    // Getter/setter functions for the input/output image path
-    void set_opath(std::string path) {this->m_opath = path;}
-    string get_opath() const {return m_opath;}
-
-private:
-    MR_3D mr;
-    FilterAnaSynt fas;
-    FilterAnaSynt *ptrfas = NULL;
-    bool mr_initialized;
-    std::string m_opath;
-    int type_of_multiresolution_transform;
-    int type_of_lifting_transform;
-    int number_of_scales;
-    int iter;
-    int type_of_filters;
-    bool use_l2_norm;
-    int nb_procs;
-    int verbose;
-
-    type_trans_3d mr_transform = TO3_MALLAT;
-    type_lift lift_transform = DEF_LIFT;
-    type_sb_filter filter = F_MALLAT_7_9;
-
-    sb_type_norm norm = NORM_L1;
-};
-
-// Constructor
-MRTransform3D::MRTransform3D(
-        int type_of_multiresolution_transform,
-        int type_of_lifting_transform,
-        int number_of_scales,
-        int iter,
-        int type_of_filters,
-        bool use_l2_norm,
-        int nb_procs,
-        int verbose){
-    // Define instance attributes
-    this->type_of_multiresolution_transform = type_of_multiresolution_transform;
-    this->type_of_lifting_transform = type_of_lifting_transform;
-    this->number_of_scales = number_of_scales;
-    this->iter = iter;
-    this->type_of_filters = type_of_filters;
-    this->use_l2_norm = use_l2_norm;
-    this->verbose = verbose;
-    this->mr_initialized = false;
-    bool use_filter = false;
-    // The maximum number of threads returned by omp_get_max_threads()
-    // (which is the default number of threads used by OMP in parallel
-    // regions) can sometimes be far below the number of CPUs.
-    // It is then required to set it in relation to the real number of CPUs
-    // (the -1 is used to live one thread to the main process which ensures
-    // better and more constant performances). - Fabrice Poupon 2013/03/09
-    #ifdef _OPENMP
-        if (nb_procs <= 0)
-            this->nb_procs = omp_get_num_procs() - 1;
-        else
-            this->nb_procs = nb_procs;
-        omp_set_num_threads(this->nb_procs);
-    #endif
-
-    // Load the mr transform
-    if ((this->type_of_multiresolution_transform > 0) && (this->type_of_multiresolution_transform <= NBR_TRANS_3D+1))
-        this->mr_transform = type_trans_3d(this->type_of_multiresolution_transform - 1);
-    else
-        throw std::invalid_argument("Invalid MR transform number.");
-
-    // Load the lifting transform
-    if ((this->type_of_lifting_transform > 0) && (this->type_of_lifting_transform <= NBR_LIFT))
-        this->lift_transform = type_lift(this->type_of_lifting_transform);
-    else
-        throw std::invalid_argument("Invalid lifting transform number.");
-
-    // Check the number of scales
-    if ((this->number_of_scales <= 1) || (this->number_of_scales > MAX_SCALE_1D))
-        throw std::invalid_argument("Bad number of scales ]1; MAX_SCALE].");
-
-    // Check the number of iterations
-    if ((this->iter <= 1) || (this->iter > 20))
-        throw std::invalid_argument("Bad number of iteration ]1; 20].");
-
-    // Load the filter
-    if (this->type_of_filters != 1){
-        std::stringstream strs;
-        strs << type_of_filters;
-        this->filter = get_filter_bank((char *)strs.str().c_str());
-        use_filter = true;
-    }
-
-    // Change the norm
-    if (this->use_l2_norm)
-        this->norm = NORM_L2;
-
-    // Check compatibility between parameters
-	if ((this->mr_transform != TO3_MALLAT) && (this->use_l2_norm || use_filter))
-        throw std::invalid_argument("transforms other than Mallat should not be used with filters and L2 norm");
-	if ((this->mr_transform != TO3_LIFTING) && (this->lift_transform != DEF_LIFT))
-	    throw std::invalid_argument("Non lifting transforms can only be used with integer Haar WT as lifting scheme:");
-
-}
-
-// Destructor
-MRTransform3D::~MRTransform3D(){
-}
-
-// Save transformation method
-void MRTransform3D::Save(MR_3D &mr){
-
-    // Welcome message
-    if (this->verbose > 0)
-        cout << "  Output path: " << this->m_opath << endl;
-
-    // Check inputs
-    if (this->m_opath == "")
-        throw std::invalid_argument(
-            "Please specify an output image path in 'opath'.");
-
-    // Write the results
-    mr.write((char *)this->m_opath.c_str());
-}
-
-void MRTransform3D::Info(){
-    // Information message
-    cout << "---------" << endl;
-    cout << "Information" << endl;
-    cout << "Runtime parameters:" << endl;
-    cout << "  Number of procs: " << this->nb_procs << endl;
-    cout << "  MR transform ID: " << this->type_of_multiresolution_transform << endl;
-    cout << "  MR transform name: " << StringTransf3D(this->mr_transform) << endl;
-    if ((this->mr_transform == TO3_MALLAT)) {
-        cout << "  Filter ID: " << this->type_of_filters << endl;
-        cout << "  Filter name: " << StringSBFilter(this->filter) << endl;
-        if (this->use_l2_norm)
-            cout << "   Use L2-norm." << endl;
-        }
-    if (this->mr_transform == TO3_LIFTING) {
-        cout << "  Lifting transform ID: " << this->type_of_lifting_transform << endl;
-        cout << "  Lifting transform name: " << StringLSTransform(this->lift_transform) << endl;
-        }
-    cout << "  Number of scales: " << this->number_of_scales << endl;
-    cout << "---------" << endl;
-    }
-
-
-// Transform method
-py::list MRTransform3D::Transform(py::array_t<float>& arr, bool save){
-
-    // Create the transformation
-    fltarray data = array2image_3d(arr);
-    if (!this->mr_initialized) {
-        if ((this->mr_transform == TO3_MALLAT)) {
-            fas.Verbose = (Bool)this->verbose;
-            fas.alloc(this->filter);
-            ptrfas = &fas;
-        }
-
-        mr.alloc(data.nx(), data.ny(), data.nz(), this->mr_transform,
-                 this->number_of_scales, ptrfas, this->norm);
-
-        if (this->mr_transform == TO3_LIFTING)
-            mr.LiftingTrans = this->lift_transform;
-
-        mr.Verbose = (Bool)this->verbose;
-        this->mr_initialized = true;
-    }
-
-    // Perform the transformation
-    if (this->verbose > 0) {
-        cout << "Starting transformation" << endl;
-        cout << "Runtime parameters:" << endl;
-        cout << "  Number of bands: " << mr.nbr_band() << endl;
-        cout << "  Data dimension: " << arr.ndim() << endl;
-        cout << "  Array shape: " << arr.shape(0) << ", " << arr.shape(1) << ", " << arr.shape(2) << endl;
-        cout << "  Save transform: " << save << endl;
-    }
-
-    ASSERT_THROW(
-        ((int)pow(2, this->number_of_scales) <= (int)min(arr.shape(0), min(arr.shape(1), arr.shape(2)))),
-        "Number of scales is too damn high (for the size of the data)");
-
-    mr.transform(data);
-
-    // Save transform if requested
-    if (save)
-        Save(mr);
-
-    // Return the generated bands data
-    py::list mr_data;
-    for (int s=0; s<mr.nbr_band(); s++) {
-        fltarray tmpband;
-        mr.get_band(s, tmpband);
-        mr_data.append(image2array_3d(tmpband));
-    }
-
-    // Get the number of bands for each scale
-    py::list mr_scale;
-
-    // WARNING: This code is a fix as the method nbr_band_per_resol() hasn't
-    // been implemented in the 3D case
-
-    // For decimated transforms Mallat and Lifting, this is constant
-    int nbr_band_per_resol_cst = 7;
-    // Idem for undecimated Atrous transform
-    if(this->mr_transform == TO3_ATROUS ){nbr_band_per_resol_cst = 1;}
-
-    int nb_bands_count = 0;
-    for (int s=0; s<mr.nbr_scale(); s++) {
-        nb_bands_count += nbr_band_per_resol_cst;
-        mr_scale.append(nbr_band_per_resol_cst);
-    }
-    if (nb_bands_count != mr.nbr_band()) {
-        mr_scale[py::len(mr_scale) - 1] = 1;
-    }
-
-    // Format the result
-    py::list mr_result;
-    mr_result.append(mr_data);
-    mr_result.append(mr_scale);
-
-    // cout << "mr_result[1]: " << py::extract<std::string>(py::object(mr_result[1]).attr("__str__")())() << endl;
-
-    return mr_result;
-}
-
-// Reconstruction method
-py::array_t<float> MRTransform3D::Reconstruct(py::list mr_data){
-    // Welcome message
-    if (this->verbose > 0) {
-        cout << "Starting Reconstruction" << endl;
-        cout << "Runtime parameters:" << endl;
-        cout << "  Number of bands: " << py::len(mr_data) << endl;
-    }
-
-    // Update transformation
-    for (int s=0; s<py::len(mr_data); s++) {
-        py::array_t<float> band_array = py::array(mr_data[s]);
-        fltarray band_data = array2image_3d(band_array);
-        // cout << "Size of inserted band ";
-        // cout << "nb_e:"<< band_data.n_elem() << "/ndim:" << band_data.naxis()\
-        // << "/nx:" << band_data.nx() << "/ny:"  << band_data.ny() << "nz:"  << band_data.nz() <<  endl;
-
-        mr.insert_band(s, band_data);
-    }
-
-    int Nx = mr.size_cube_nx();
-    int Ny = mr.size_cube_ny();
-    int Nz = mr.size_cube_nz();
-
-    // Start the reconstruction
-    fltarray data(Nx, Ny, Nz, "Reconstruct");
-    mr.recons(data);
-
-    return image2array_3d(data);
-}
-
-#endif


Test patch:
diff --git a/pysap/test/test_binding.py b/pysap/test/test_binding.py
index 12997fc5..31b60d6d 100644
--- a/pysap/test/test_binding.py
+++ b/pysap/test/test_binding.py
@@ -8,11 +8,12 @@
 ##########################################################################
 
 # System import
-import unittest
 import os
 import numpy
 import sys
 import time
+import pytest
+from unittest import TestCase
 
 
 # Package import
@@ -21,11 +22,11 @@
 import pysap.extensions.sparse2d as sp
 from pysap.extensions import tools
 from pysap.data import get_sample_data
-from numpy.testing import assert_raises
+import numpy.testing as npt
 from astropy.io import fits
 
 
-class TestWarpAndBinding(unittest.TestCase):
+class TestWarpAndBinding(TestCase):
     """ Test the analysis/synthesis of an input image using a wrapping or a
     binding strategy.
     """
@@ -160,7 +161,7 @@ def test_filter_init(self):
         flt = sp.Filter()
         data = numpy.copy(self.images[0])
         flt.filter(data)
-        assert(flt.data is not None)
+        assert flt.data is not None
 
     def test_filter_default(self):
         # filter with binding
@@ -256,16 +257,16 @@ def test_filter_options_f3_n5(self):
 
     def test_filter_noise_valueerror(self):
         data = numpy.copy(self.images[1])
-        with assert_raises(ValueError):
+        with npt.assert_raises(ValueError):
             flt = sp.Filter(epsilon_poisson=5, type_of_noise=2)
             flt.filter(data)
-        with assert_raises(ValueError):
+        with npt.assert_raises(ValueError):
             flt = sp.Filter(type_of_noise=9)
             flt.filter(data)
         with pysap.TempDir(isap=True) as tmpdir:
             in_image = os.path.join(tmpdir, "in.fits")
             pysap.io.save(data, in_image)
-        with assert_raises(ValueError):
+        with npt.assert_raises(ValueError):
             flt = sp.Filter(rms_map=in_image, type_of_noise=6)
             flt.filter(data)
 
@@ -274,7 +275,7 @@ def test_deconv_init(self):
         data = self.deconv_images[0].data
         psf = self.deconv_images[1].data
         deconv.deconvolve(data, psf)
-        assert(deconv.data is not None)
+        assert deconv.data is not None
 
     def test_deconv_default(self):
         deconv = sp.Deconvolve()
@@ -339,6 +340,10 @@ def test_deconv_d1_t20(self):
             diff = deconv.data.data - image
             self.assertFalse(diff.all())
 
+    @pytest.mark.xfail(
+        run=False,
+        reason="Raises segmentation fault, Sparse2D issue open to resolve."
+    )
     def test_deconv_u3_d5_n2_p_g5(self):
         deconv = sp.Deconvolve(number_of_undecimated_scales=3,
                                type_of_deconvolution=5,
@@ -424,8 +429,9 @@ def test_mr2d1d_init(self):
 
         mr = sp.MR2D1D()
         mr.transform(data)
-        assert(mr.cube is not None)
+        assert mr.cube is not None
 
+    @pytest.mark.xfail(reason="Test fails, Sparse2D issue open to resolve.")
     def test_mr2d1d_output_val(self):
         data = self.mr_image.data
 
@@ -441,8 +447,13 @@ def test_mr2d1d_output_val(self):
             pysap.extensions.mr2d1d_trans(in_image, out_file,
                                           type_of_multiresolution_transform=2)
             im_wrap = numpy.copy(pysap.io.load(out_file))
-        assert(numpy.isclose(mr.cube, im_wrap, atol=0.00001).all())
+        npt.assert_almost_equal(
+            mr.cube,
+            im_wrap,
+            err_msg='Incorrect mr2d1d output.',
+        )
 
+    @pytest.mark.xfail(reason="Test fails, Sparse2D issue open to resolve.")
     def test_mr2d1d_normalized(self):
         data = self.mr_image.data
 
@@ -457,8 +468,14 @@ def test_mr2d1d_normalized(self):
             pysap.io.save(data, in_image)
             pysap.extensions.mr2d1d_trans(in_image, out_file, normalize=True)
             im_wrap = numpy.copy(pysap.io.load(out_file))
-        assert(numpy.isclose(mr.cube, im_wrap, atol=0.00001).all())
-
+        npt.assert_almost_equal(
+            mr.cube,
+            im_wrap,
+            decimal=5,
+            err_msg='Incorrect mr2d1d normalized.',
+        )
+
+    @pytest.mark.xfail(reason="Test fails, Sparse2D issue open to resolve.")
     def test_mr2d1d_scales(self):
         data = self.mr_image.data
 
@@ -475,7 +492,11 @@ def test_mr2d1d_scales(self):
                                           number_of_scales_2D=2,
                                           number_of_scales_1D=6)
             im_wrap = numpy.copy(pysap.io.load(out_file))
-        assert(numpy.isclose(mr.cube, im_wrap, atol=0.00001).all())
+        npt.assert_almost_equal(
+            mr.cube,
+            im_wrap,
+            err_msg='Incorrect mr2d1d scales.',
+        )
 
     def test_mr2d1d_recons(self):
         data = self.mr_image.data
@@ -494,8 +515,8 @@ def test_mr2d1d_recons(self):
             pysap.extensions.mr2d1d_trans(out_file, "recons.fits",
                                           reverse=True)
             im_wrap = numpy.copy(pysap.io.load("recons.fits"))
-        assert(numpy.isclose(mr.recons, im_wrap, atol=0.00001).all())
-
-
-if __name__ == "__main__":
-    unittest.main()
+        npt.assert_almost_equal(
+            mr.recons,
+            im_wrap,
+            err_msg='Incorrect mr2d1d reconstruction.',
+        )