diff --git a/.gitignore b/.gitignore index e6e4e0f3728..915fe5b0f78 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ torchvision/version.py */**/*~ *~ docs/build +docs/source/auto_examples/ +docs/source/gen_modules/ .coverage htmlcov .*.swp diff --git a/docs/Makefile b/docs/Makefile index 1cacf08002f..58daa471f5c 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -19,6 +19,15 @@ docset: html cp $(SPHINXPROJ).docset/icon.png $(SPHINXPROJ).docset/icon@2x.png convert $(SPHINXPROJ).docset/icon@2x.png -resize 16x16 $(SPHINXPROJ).docset/icon.png +html-noplot: # Avoids running the gallery examples, which may take time + $(SPHINXBUILD) -D plot_gallery=0 -b html $(ASPHINXOPTS) "$(BUILDDIR)"/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +clean: + rm -rf $(BUILDDIR)/* + rm -rf auto_examples/ + .PHONY: help Makefile docset # Catch-all target: route all unknown targets to Sphinx using the new diff --git a/docs/requirements.txt b/docs/requirements.txt index 77ea4d276c8..6fda04707ea 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,5 @@ -sphinx +sphinx==2.4.4 +sphinx-gallery +matplotlib +numpy -e git+git://github.com/pytorch/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme diff --git a/docs/source/conf.py b/docs/source/conf.py index 1d2b59f48de..60994eae8b9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -20,6 +20,10 @@ # import os # import sys # sys.path.insert(0, os.path.abspath('.')) + +from pathlib import Path +import os + import torch import torchvision import pytorch_sphinx_theme @@ -44,8 +48,16 @@ 'sphinx.ext.mathjax', 'sphinx.ext.napoleon', 'sphinx.ext.viewcode', + 'sphinx_gallery.gen_gallery', ] +sphinx_gallery_conf = { + 'examples_dirs': '../../gallery/', # path to your example scripts + 'gallery_dirs': 'auto_examples', # path to where to save gallery generated output + 'backreferences_dir': 'gen_modules/backreferences', + 'doc_module': ('torchvision',), +} + napoleon_use_ivar = True napoleon_numpy_docstring = False napoleon_google_docstring = True @@ -248,3 +260,42 @@ def handle_item(fieldarg, content): TypedField.make_field = patched_make_field + + +def inject_minigalleries(app, what, name, obj, options, lines): + """Inject a minigallery into a docstring. + + This avoids having to manually write the .. minigallery directive for every item we want a minigallery for, + as it would be easy to miss some. + + This callback is called after the .. auto directives (like ..autoclass) have been processed, + and modifies the lines parameter inplace to add the .. minigallery that will show which examples + are using which object. + + It's a bit hacky, but not *that* hacky when you consider that the recommended way is to do pretty much the same, + but instead with templates using autosummary (which we don't want to use): + (https://sphinx-gallery.github.io/stable/configuration.html#auto-documenting-your-api-with-links-to-examples) + + For docs on autodoc-process-docstring, see the autodoc docs: + https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html + """ + + conf_file_path = Path(__file__).parent.absolute() + backrefs_path = conf_file_path / sphinx_gallery_conf['backreferences_dir'] / (name + '.examples') + if not (os.path.isfile(backrefs_path) and os.path.getsize(backrefs_path) > 0): + # We avoid showing the (empty) minigallery if there's nothing to show, i.e. if the object + # isn't used in any example. + # FIXME: this check can be removed once https://github.com/sphinx-gallery/sphinx-gallery/pull/813 + # is merged and the new sphinx-gallery version (> 0.8.x) is released. + return + + if what in ("class", "function"): + lines.append(f".. minigallery:: {name}") + lines.append(f" :add-heading: Examples using ``{name.split('.')[-1]}``:") + # avoid heading entirely to avoid warning. As a bonud it actually renders better + lines.append(" :heading-level: 9") + lines.append("\n") + + +def setup(app): + app.connect('autodoc-process-docstring', inject_minigalleries) diff --git a/docs/source/index.rst b/docs/source/index.rst index d4aefafed1d..61cb573c96f 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -38,6 +38,12 @@ architectures, and common image transformations for computer vision. transforms utils +.. toctree:: + :maxdepth: 1 + :caption: Examples + + auto_examples/index + .. automodule:: torchvision :members: diff --git a/gallery/README.rst b/gallery/README.rst new file mode 100644 index 00000000000..319052106b5 --- /dev/null +++ b/gallery/README.rst @@ -0,0 +1,4 @@ +Example gallery +=============== + +Below is a gallery of examples \ No newline at end of file diff --git a/gallery/assets/astronaut.jpg b/gallery/assets/astronaut.jpg new file mode 100644 index 00000000000..9716f656269 Binary files /dev/null and b/gallery/assets/astronaut.jpg differ diff --git a/gallery/plot_transforms.py b/gallery/plot_transforms.py new file mode 100644 index 00000000000..44f4f872fd3 --- /dev/null +++ b/gallery/plot_transforms.py @@ -0,0 +1,64 @@ +""" +========================== +Illustration of transforms +========================== + +This example illustrates the various transforms available in :mod:`torchvision.transforms`. +""" + +from PIL import Image +from pathlib import Path +import matplotlib.pyplot as plt +import numpy as np + +import torchvision.transforms as T + + +orig_img = Image.open(Path('assets') / 'astronaut.jpg') + + +def plot(img, title="", with_orig=True, **kwargs): + def _plot(img, title, **kwargs): + plt.figure().suptitle(title, fontsize=25) + plt.imshow(np.asarray(img), **kwargs) + plt.axis('off') + + if with_orig: + _plot(orig_img, "Original image") + _plot(img, title, **kwargs) + + +#################################### +# Pad +# --- +# The :class:`~torchvision.transforms.Pad` transform +# (see also :func:`~torchvision.transforms.functional.pad`) +# fills image borders with some pixel values. +padded_img = T.Pad(padding=30)(orig_img) +plot(padded_img, "Padded image") + +#################################### +# Resize +# ------ +# The :class:`~torchvision.transforms.Resize` transform +# (see also :func:`~torchvision.transforms.functional.resize`) +# resizes an image. +resized_img = T.Resize(size=30)(orig_img) +plot(resized_img, "Resized image") + +#################################### +# ColorJitter +# ----------- +# The :class:`~torchvision.transforms.ColorJitter` transform +# randomly changes the brightness, saturation, and other properties of an image. +jitted_img = T.ColorJitter(brightness=.5, hue=.3)(orig_img) +plot(jitted_img, "Jitted image") + +#################################### +# Grayscale +# --------- +# The :class:`~torchvision.transforms.Grayscale` transform +# (see also :func:`~torchvision.transforms.functional.to_grayscale`) +# converts an image to grayscale +gray_img = T.Grayscale()(orig_img) +plot(gray_img, "Grayscale image", cmap='gray')