diff --git a/.gitignore b/.gitignore
index fd1b966..ebeaf9a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,7 +69,11 @@ instance/
.scrapy
# Sphinx documentation
+docs/build/
docs/_build/
+docs/html/
+docs/_autosummary/
+docs/src/
# PyBuilder
target/
@@ -131,3 +135,6 @@ dmypy.json
# Pyre type checker
.pyre/
+
+# macOS
+*.DS_Store
\ No newline at end of file
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..ed88099
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = .
+BUILDDIR = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..1804ba7
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,30 @@
+## Documentations
+
+### Build
+
+First install requirements for building docs:
+```
+python -m pip install -r docs/requirements.txt
+```
+
+Docs can then be built running the commands:
+
+```
+cd docs/
+sphinx-apidoc -f -o src/ ../torchstain
+make html
+```
+
+### Usage
+
+To access the documentations, open the generated HTML in your browser, e.g., with firefox on Linux run this:
+```
+firefox build/html/index.html
+```
+
+Alternatively, on macOS you can open the HTML in chrome by:
+```
+open -a "Google Chrome" build/html/index.html
+```
+
+When documentations are pushed to production, they will be available at [torchstain.readthedocs.io/](https://torchstain.readthedocs.io/).
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..228cf9d
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,82 @@
+"""Configuration file for the Sphinx documentation builder."""
+
+import inspect
+import os
+import subprocess
+import sys
+
+import torchstain
+
+
+sys.path.insert(0, os.path.abspath('..'))
+
+
+# Project information
+url = "https://github.com/EIDOSLAB/torchstain"
+
+# General configuration
+master_doc = 'index'
+
+# Sphinx extension modules
+extensions = [
+ 'sphinx.ext.napoleon',
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.intersphinx',
+ 'sphinx.ext.linkcode',
+]
+
+# Generate autosummary
+autosummary_generate = True
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files
+exclude_patterns = ['templates']
+
+# numpy style docs with Napoleon
+napoleon_google_docstring = False
+napoleon_use_param = False
+napoleon_use_ivar = True
+
+# Draw graphs in the SVG format instead of the default PNG format
+graphviz_output_format = 'svg'
+
+# sphinx.ext.linkcode: Try to link to source code on GitHub
+REVISION_CMD = ['git', 'rev-parse', '--short', 'HEAD']
+try:
+ _git_revision = subprocess.check_output(REVISION_CMD).strip()
+except (subprocess.CalledProcessError, OSError):
+ _git_revision = 'master'
+else:
+ _git_revision = _git_revision.decode('utf-8')
+
+
+def linkcode_resolve(domain, info):
+ if domain != 'py':
+ return None
+ module = info.get('module', None)
+ fullname = info.get('fullname', None)
+ if not module or not fullname:
+ return None
+ obj = sys.modules.get(module, None)
+ if obj is None:
+ return None
+
+ for part in fullname.split('.'):
+ obj = getattr(obj, part)
+ if isinstance(obj, property):
+ obj = obj.fget
+ if hasattr(obj, '__wrapped__'):
+ obj = obj.__wrapped__
+
+ file = inspect.getsourcefile(obj)
+ package_dir = os.path.dirname(torchstain.__file__)
+ if file is None or os.path.commonpath([file, package_dir]) != package_dir:
+ return None
+ file = os.path.relpath(file, start=package_dir)
+ source, line_start = inspect.getsourcelines(obj)
+ line_end = line_start + len(source) - 1
+ filename = f'src/torchstain/{file}#L{line_start}-L{line_end}'
+ return f'{url}/blob/{_git_revision}/{filename}'
diff --git a/docs/examples/stain_normalization.rst b/docs/examples/stain_normalization.rst
new file mode 100644
index 0000000..de17261
--- /dev/null
+++ b/docs/examples/stain_normalization.rst
@@ -0,0 +1,61 @@
+Stain Normalization
+===================
+
+The torchstain package supports three different backends: PyTorch,
+TensorFlow, and NumPy. Below is a simple example of how to get started.
+In this example we use PyTorch.
+
+To run example, be sure to have installed the necessary dependencies:
+
+``pip install torchstain[torch] torchvision opencv-python``
+
+A simple usage example can be seen below:
+
+.. code-block:: python
+
+ import torch
+ from torchvision import transforms
+ import torchstain
+ import cv2
+
+ target = cv2.cvtColor(cv2.imread("./data/target.png"), cv2.COLOR_BGR2RGB)
+ to_transform = cv2.cvtColor(cv2.imread("./data/source.png"), cv2.COLOR_BGR2RGB)
+
+ T = transforms.Compose([
+ transforms.ToTensor(),
+ transforms.Lambda(lambda x: x*255)
+ ])
+
+ normalizer = torchstain.normalizers.MacenkoNormalizer(backend='torch')
+ normalizer.fit(T(target))
+
+ t_to_transform = T(to_transform)
+ norm, H, E = normalizer.normalize(I=t_to_transform, stains=True)
+
+
+The generated result can be seen below:
+
+.. image:: ../../data/result.png
+ :alt: Stain normalized result
+
+
+Different Backends
+------------------
+
+To use TensorFlow or NumPy backend, simply change the *backend*
+argument to the *MacenkoNormalizer*. Also note that different for
+different backends and normalization techniques, different
+preprocessing may be required.
+
+For TensorFlow instead perform:
+
+.. code-block:: python
+
+ import tensorflow as tf
+ import numpy as np
+
+ T = lambda x: tf.convert_to_tensor(np.moveaxis(x, -1, 0).astype("float32"))
+ t_to_transform = T(to_transform)
+
+Whereas for NumPy *no* preprocessing is required, given that the image
+is already channel-last and uint8 dtype.
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..7227d63
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,61 @@
+:github_url: https://github.com/EIDOSLAB/torchstain/tree/main/docs
+
+TorchStain
+-------------------
+
+TorchStain is a modular Python package for GPU-accelerated stain normalization
+and augmentation for histopathological image analysis. It supports PyTorch,
+TensorFlow, and NumPy backends.
+
+Installation
+------------
+
+The latest release of TorchStain can be installed from
+`PyPI `_ by:
+
+``pip install torchstain``
+
+To install a specific backend use either torchstain[torch] or torchstain[tf].
+The numpy backend is included by default in both.
+
+You may also install directly from GitHub, using the following command:
+
+``pip install git+https://github.com/EIDOSLAB/torchstain``
+
+.. toctree::
+ :glob:
+ :caption: Examples
+ :maxdepth: 2
+
+ examples/*
+
+.. toctree::
+ :caption: API Documentation
+ :maxdepth: 10
+
+ src/modules
+
+The Team
+--------
+
+The development of TorchStain is led by researchers at `EIDOSLAB `_
+and `SINTEF MIA `_.
+We are also very grateful to the open source community for contributing ideas, bug fixes, and issues.
+
+Support
+-------
+
+If you are having issues, please let us know by filing an issue on our
+`issue tracker `_.
+
+
+License
+-------
+
+TorchStain is licensed under the `MIT License `_.
+
+
+Indices and Tables
+==================
+
+* :ref:`genindex`
\ No newline at end of file
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..8e99ff9
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,5 @@
+sphinx==5.3.0
+sphinx-rtd-theme
+torchstain
+tensorflow
+torch
diff --git a/docs/templates/class.rst b/docs/templates/class.rst
new file mode 100644
index 0000000..b55ecb3
--- /dev/null
+++ b/docs/templates/class.rst
@@ -0,0 +1,32 @@
+{{ fullname | escape | underline}}
+
+.. currentmodule:: {{ module }}
+
+.. autoclass:: {{ objname }}
+ :members: <-- add at least this line
+ :show-inheritance: <-- plus I want to show inheritance...
+ :inherited-members: <-- ...and inherited members too
+
+ {% block methods %}
+ .. automethod:: __init__
+
+ {% if methods %}
+ .. rubric:: {{ _('Methods') }}
+
+ .. autosummary::
+ {% for item in methods %}
+ ~{{ name }}.{{ item }}
+ {%- endfor %}
+ {% endif %}
+ {% endblock %}
+
+ {% block attributes %}
+ {% if attributes %}
+ .. rubric:: {{ _('Attributes') }}
+
+ .. autosummary::
+ {% for item in attributes %}
+ ~{{ name }}.{{ item }}
+ {%- endfor %}
+ {% endif %}
+ {% endblock %}
\ No newline at end of file
diff --git a/docs/templates/module.rst b/docs/templates/module.rst
new file mode 100644
index 0000000..f22aac8
--- /dev/null
+++ b/docs/templates/module.rst
@@ -0,0 +1,66 @@
+{{ fullname | escape | underline}}
+
+.. automodule:: {{ fullname }}
+
+ {% block attributes %}
+ {% if attributes %}
+ .. rubric:: Module Attributes
+
+ .. autosummary::
+ :toctree: <-- add this line
+ {% for item in attributes %}
+ {{ item }}
+ {%- endfor %}
+ {% endif %}
+ {% endblock %}
+
+ {% block functions %}
+ {% if functions %}
+ .. rubric:: {{ _('Functions') }}
+
+ .. autosummary::
+ :toctree: <-- add this line
+ {% for item in functions %}
+ {{ item }}
+ {%- endfor %}
+ {% endif %}
+ {% endblock %}
+
+ {% block classes %}
+ {% if classes %}
+ .. rubric:: {{ _('Classes') }}
+
+ .. autosummary::
+ :toctree: <-- add this line
+ :template: class.rst <-- add this line
+ {% for item in classes %}
+ {{ item }}
+ {%- endfor %}
+ {% endif %}
+ {% endblock %}
+
+ {% block exceptions %}
+ {% if exceptions %}
+ .. rubric:: {{ _('Exceptions') }}
+
+ .. autosummary::
+ :toctree: <-- add this line
+ {% for item in exceptions %}
+ {{ item }}
+ {%- endfor %}
+ {% endif %}
+ {% endblock %}
+
+{% block modules %}
+{% if modules %}
+.. rubric:: Modules
+
+.. autosummary::
+ :toctree:
+ :template: class.rst <-- add this line
+ :recursive:
+{% for item in modules %}
+ {{ item }}
+{%- endfor %}
+{% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/readthedocs.yml b/readthedocs.yml
new file mode 100644
index 0000000..d165f5b
--- /dev/null
+++ b/readthedocs.yml
@@ -0,0 +1,23 @@
+# .readthedocs.yaml
+
+# required
+version: 2
+
+# set the version of Python and other tools you might need
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.11"
+
+# build documentation in the docs/ directory with Sphinx
+sphinx:
+ builder: html
+ configuration: docs/conf.py
+ fail_on_warning: false
+
+# declare the Python requirements required to build your docs
+python:
+ install:
+ - requirements: docs/requirements.txt
+ - method: pip
+ path: .