From 7b9338d3578d1091b823450b74357ac6fb5e0694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Pedersen?= Date: Fri, 2 Jun 2023 17:33:11 +0200 Subject: [PATCH 1/4] Docs: Setup test Documentations [no ci] --- docs/Makefile | 20 ++++++++++ docs/README.md | 24 +++++++++++ docs/api.rst | 11 +++++ docs/conf.py | 80 +++++++++++++++++++++++++++++++++++++ docs/index.rst | 76 +++++++++++++++++++++++++++++++++++ docs/requirements.txt | 2 + docs/templates/class.rst | 9 +++++ docs/templates/function.rst | 6 +++ readthedocs.yml | 23 +++++++++++ 9 files changed, 251 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/README.md create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/requirements.txt create mode 100644 docs/templates/class.rst create mode 100644 docs/templates/function.rst create mode 100644 readthedocs.yml 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..e090cd1 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,24 @@ +## Documentations + +### Build + +Docs can be built by running the commands: + +``` +cd docs/ +python -m sphinx -T -E -b html -d _build/doctrees -D language=en . 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/api.rst b/docs/api.rst new file mode 100644 index 0000000..2d86448 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,11 @@ +Stain Normalization +=================== + +.. currentmodule:: torchstain + +.. autosummary:: + + TorchMacenkoNormalizer + TensorFlowMacenkoNormalizer + NumpyMacenkoNormalizer + diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..77410e8 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,80 @@ +"""Configuration file for the Sphinx documentation builder.""" + +import inspect +import os +import subprocess +import sys + +import gradient_accumulator + + +# 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.autosummary', + 'sphinx.ext.intersphinx', + 'sphinx.ext.linkcode', +] + +# 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 = ['build'] + +# Options for the intersphinx extension +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), +} + +# Draw graphs in the SVG format instead of the default PNG format +graphviz_output_format = 'svg' + +# Generate autosummary +autosummary_generate = True + +# 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(gradient_accumulator.__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/gradient_accumulator/{file}#L{line_start}-L{line_end}' + return f'{url}/blob/{_git_revision}/{filename}' diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..909a588 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,76 @@ +: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 `_ using + +``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: Background + :maxdepth: 2 + + background/* + +.. toctree:: + :glob: + :caption: Examples + :maxdepth: 2 + + examples/* + +.. toctree:: + :glob: + :caption: Frequently Asked Questions + :maxdepth: 2 + + faq/* + + +.. toctree:: + :caption: API Documentation + :maxdepth: 2 + + api + +The Team +-------- + +The development of TorchStain is led by researchers at [EIDOSLAB](https://eidos.di.unito.it/) +and [SINTEF MIA](https://www.sintef.no/en/expertise/sintef-technology-and-society/medical-technology/). +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..48c651c --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +sphinx==5.3.0 +sphinx-rtd-theme diff --git a/docs/templates/class.rst b/docs/templates/class.rst new file mode 100644 index 0000000..21685f8 --- /dev/null +++ b/docs/templates/class.rst @@ -0,0 +1,9 @@ +:mod:`{{module}}`.{{objname}} +{{ underline }}============== + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :show-inheritance: + :members: + :inherited-members: diff --git a/docs/templates/function.rst b/docs/templates/function.rst new file mode 100644 index 0000000..53e3709 --- /dev/null +++ b/docs/templates/function.rst @@ -0,0 +1,6 @@ +:mod:`{{module}}`.{{objname}} +{{ underline }}============== + +.. currentmodule:: {{ module }} + +.. autofunction:: {{ objname }} 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: . From 2ca2d40f7cb95745e23301c2b0a893c55151c450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Pedersen?= Date: Fri, 2 Jun 2023 19:18:55 +0200 Subject: [PATCH 2/4] Docs: Refactored + added API autodocs --- .gitignore | 7 ++++ docs/README.md | 10 ++++-- docs/api.rst | 11 ------- docs/conf.py | 26 ++++++++------- docs/index.rst | 31 +++-------------- docs/requirements.txt | 3 ++ docs/templates/class.rst | 33 ++++++++++++++++--- docs/templates/function.rst | 6 ---- docs/templates/module.rst | 66 +++++++++++++++++++++++++++++++++++++ 9 files changed, 131 insertions(+), 62 deletions(-) delete mode 100644 docs/api.rst delete mode 100644 docs/templates/function.rst create mode 100644 docs/templates/module.rst 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/README.md b/docs/README.md index e090cd1..1804ba7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,11 +2,17 @@ ### Build -Docs can be built by running the commands: +First install requirements for building docs: +``` +python -m pip install -r docs/requirements.txt +``` + +Docs can then be built running the commands: ``` cd docs/ -python -m sphinx -T -E -b html -d _build/doctrees -D language=en . html +sphinx-apidoc -f -o src/ ../torchstain +make html ``` ### Usage diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index 2d86448..0000000 --- a/docs/api.rst +++ /dev/null @@ -1,11 +0,0 @@ -Stain Normalization -=================== - -.. currentmodule:: torchstain - -.. autosummary:: - - TorchMacenkoNormalizer - TensorFlowMacenkoNormalizer - NumpyMacenkoNormalizer - diff --git a/docs/conf.py b/docs/conf.py index 77410e8..228cf9d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,7 +5,10 @@ import subprocess import sys -import gradient_accumulator +import torchstain + + +sys.path.insert(0, os.path.abspath('..')) # Project information @@ -18,29 +21,28 @@ extensions = [ 'sphinx.ext.napoleon', 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', '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 = ['build'] +exclude_patterns = ['templates'] -# Options for the intersphinx extension -intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), -} +# 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' -# Generate autosummary -autosummary_generate = True - # sphinx.ext.linkcode: Try to link to source code on GitHub REVISION_CMD = ['git', 'rev-parse', '--short', 'HEAD'] try: @@ -70,11 +72,11 @@ def linkcode_resolve(domain, info): obj = obj.__wrapped__ file = inspect.getsourcefile(obj) - package_dir = os.path.dirname(gradient_accumulator.__file__) + 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/gradient_accumulator/{file}#L{line_start}-L{line_end}' + filename = f'src/torchstain/{file}#L{line_start}-L{line_end}' return f'{url}/blob/{_git_revision}/{filename}' diff --git a/docs/index.rst b/docs/index.rst index 909a588..cd8f0b0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,7 +11,7 @@ Installation ------------ The latest release of TorchStain can be installed from -`PyPI `_ using +`PyPI `_ by: ``pip install torchstain`` @@ -22,39 +22,18 @@ You may also install directly from GitHub, using the following command: ``pip install git+https://github.com/EIDOSLAB/torchstain`` -.. toctree:: - :glob: - :caption: Background - :maxdepth: 2 - - background/* - -.. toctree:: - :glob: - :caption: Examples - :maxdepth: 2 - - examples/* - -.. toctree:: - :glob: - :caption: Frequently Asked Questions - :maxdepth: 2 - - faq/* - .. toctree:: :caption: API Documentation - :maxdepth: 2 + :maxdepth: 10 - api + src/modules The Team -------- -The development of TorchStain is led by researchers at [EIDOSLAB](https://eidos.di.unito.it/) -and [SINTEF MIA](https://www.sintef.no/en/expertise/sintef-technology-and-society/medical-technology/). +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 diff --git a/docs/requirements.txt b/docs/requirements.txt index 48c651c..8e99ff9 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,5 @@ sphinx==5.3.0 sphinx-rtd-theme +torchstain +tensorflow +torch diff --git a/docs/templates/class.rst b/docs/templates/class.rst index 21685f8..b55ecb3 100644 --- a/docs/templates/class.rst +++ b/docs/templates/class.rst @@ -1,9 +1,32 @@ -:mod:`{{module}}`.{{objname}} -{{ underline }}============== +{{ fullname | escape | underline}} .. currentmodule:: {{ module }} .. autoclass:: {{ objname }} - :show-inheritance: - :members: - :inherited-members: + :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/function.rst b/docs/templates/function.rst deleted file mode 100644 index 53e3709..0000000 --- a/docs/templates/function.rst +++ /dev/null @@ -1,6 +0,0 @@ -:mod:`{{module}}`.{{objname}} -{{ underline }}============== - -.. currentmodule:: {{ module }} - -.. autofunction:: {{ objname }} 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 From cc5f02ad3638ebdfc888872246cc53f3cef29988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Pedersen?= Date: Fri, 2 Jun 2023 19:19:52 +0200 Subject: [PATCH 3/4] Docs: Fixed redirect in Index --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index cd8f0b0..11dfae0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -40,7 +40,7 @@ Support ------- If you are having issues, please let us know by filing an issue on our -`issue tracker `_. +`issue tracker `_. License From a644c12878491beb8658ef9e32aed2ac80fedf72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Pedersen?= Date: Fri, 2 Jun 2023 19:42:45 +0200 Subject: [PATCH 4/4] Docs: Added stain norm example --- docs/examples/stain_normalization.rst | 61 +++++++++++++++++++++++++++ docs/index.rst | 6 +++ 2 files changed, 67 insertions(+) create mode 100644 docs/examples/stain_normalization.rst 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 index 11dfae0..7227d63 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,6 +22,12 @@ 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