From c500afd437cbd87d72a8b4f5c4de2ba4d8f889a4 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 19:57:52 +0200 Subject: [PATCH 01/46] Add docs makefile --- docs/Makefile | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/Makefile diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..69fe55ec --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SOURCEDIR = source +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) \ No newline at end of file From e33e6f392739119110a09ce6545f88d3f06ac934 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 19:58:32 +0200 Subject: [PATCH 02/46] add windows make file --- docs/make.bat | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/make.bat diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..8710393c --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd \ No newline at end of file From c864d2389b208ae1f88658634e9497e47b851649 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:01:24 +0200 Subject: [PATCH 03/46] add requirements --- docs/requirements.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..e3a0a12e --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,12 @@ +sphinx>=2.0, <3.0 +recommonmark # fails with badges +m2r # fails with multi-line text +nbsphinx +pandoc +docutils +sphinxcontrib-fulltoc +sphinxcontrib-mockautodoc +git+https://github.com/pytorch/pytorch_sphinx_theme.git +# pip_shims +sphinx-autodoc-typehints +sphinx-paramlinks<0.4.0 \ No newline at end of file From 26d1583bd34e697ac5dc03dbce717f27a11c9403 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:01:35 +0200 Subject: [PATCH 04/46] add build script --- docs/build_docs.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 docs/build_docs.sh diff --git a/docs/build_docs.sh b/docs/build_docs.sh new file mode 100644 index 00000000..609f4a8e --- /dev/null +++ b/docs/build_docs.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +make clean ; make html --debug --jobs 2 SPHINXOPTS="-W" \ No newline at end of file From c1e334ea6561650d2c1a4f3eb9eb02dc06833c7f Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:01:46 +0200 Subject: [PATCH 05/46] move images --- .../images/rising_logo.png} | Bin docs/{_static => source/images}/rising_logo.svg | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/{_static/rising_Logo.png => source/images/rising_logo.png} (100%) rename docs/{_static => source/images}/rising_logo.svg (100%) diff --git a/docs/_static/rising_Logo.png b/docs/source/images/rising_logo.png similarity index 100% rename from docs/_static/rising_Logo.png rename to docs/source/images/rising_logo.png diff --git a/docs/_static/rising_logo.svg b/docs/source/images/rising_logo.svg similarity index 100% rename from docs/_static/rising_logo.svg rename to docs/source/images/rising_logo.svg From a1427b0cb25821f8a8608f2fb0a0fd16b892d6ce Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:03:03 +0200 Subject: [PATCH 06/46] adjust image path in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 195cfb34..9695903e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![logo](docs/_static/rising_logo.svg "rising") +![logo](docs/source/images/rising_logo.svg "rising") ![Project Status](https://img.shields.io/badge/status-alpha-red) ![PyPI](https://img.shields.io/pypi/v/rising) @@ -11,9 +11,9 @@ This is an alpha release which is highly experimental. All transforms should be | Python Version | Platform | Unittests | NotebookTests | |----------------------------------------------------------------|------------------------------------------------------|---------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| -| ![Python](https://img.shields.io/badge/python-3.6/3.7/3.8-red) | ![System](https://img.shields.io/badge/Linux-blue) | ![Unittests Linux](https://github.com/PhoenixDL/rising/workflows/Unittests%20Linux/badge.svg) | ![NotebookTests](https://github.com/PhoenixDL/rising/workflows/NotebookTests/badge.svg) | -| ![Python](https://img.shields.io/badge/python-3.6/3.7/3.8-red) | ![System](https://img.shields.io/badge/Windows-blue) | ![Unittests Windows](https://github.com/PhoenixDL/rising/workflows/Unittests%20Windows/badge.svg) | / | -| ![Python](https://img.shields.io/badge/python-3.6/3.7/3.8-red) | ![System](https://img.shields.io/badge/MacOS-blue) | ![Unittests macOS](https://github.com/PhoenixDL/rising/workflows/Unittests%20MacOS/badge.svg) | / | +| ![Python](https://img.shields.io/badge/python-3.6/3.7/3.8-blue) | ![System](https://img.shields.io/badge/Linux-blue) | ![Unittests Linux](https://github.com/PhoenixDL/rising/workflows/Unittests%20Linux/badge.svg) | ![NotebookTests](https://github.com/PhoenixDL/rising/workflows/NotebookTests/badge.svg) | +| ![Python](https://img.shields.io/badge/python-3.6/3.7/3.8-blue) | ![System](https://img.shields.io/badge/Windows-blue) | ![Unittests Windows](https://github.com/PhoenixDL/rising/workflows/Unittests%20Windows/badge.svg) | / | +| ![Python](https://img.shields.io/badge/python-3.6/3.7/3.8-blue) | ![System](https://img.shields.io/badge/MacOS-blue) | ![Unittests macOS](https://github.com/PhoenixDL/rising/workflows/Unittests%20MacOS/badge.svg) | / | ## What is `rising`? Rising is a high-performance data loading and augmentation library for 2D *and* 3D data completely written in PyTorch. From 97fc3ac269e28fc1daca4eded018ff583ab92e2b Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:40:56 +0200 Subject: [PATCH 07/46] move image path --- docs/source/images/{ => logo}/rising_logo.png | Bin docs/source/images/{ => logo}/rising_logo.svg | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/source/images/{ => logo}/rising_logo.png (100%) rename docs/source/images/{ => logo}/rising_logo.svg (100%) diff --git a/docs/source/images/rising_logo.png b/docs/source/images/logo/rising_logo.png similarity index 100% rename from docs/source/images/rising_logo.png rename to docs/source/images/logo/rising_logo.png diff --git a/docs/source/images/rising_logo.svg b/docs/source/images/logo/rising_logo.svg similarity index 100% rename from docs/source/images/rising_logo.svg rename to docs/source/images/logo/rising_logo.svg From 716dabda5aa19ced6ab99f1ce4a24715dc220528 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:46:02 +0200 Subject: [PATCH 08/46] initial doc config --- docs/source/conf.py | 386 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 docs/source/conf.py diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..79d7ea36 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,386 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# 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. + +import os +import sys +import glob +import shutil +import inspect + +# import m2r +import builtins +import pytorch_sphinx_theme + +PATH_HERE = os.path.abspath(os.path.dirname(__file__)) +PATH_ROOT = os.path.join(PATH_HERE, '..', '..') +sys.path.insert(0, os.path.abspath(PATH_ROOT)) + +import rising # noqa: E402 + +# -- Project documents ------------------------------------------------------- + +# # export the documentation +# with open('intro.rst', 'w') as fp: +# intro = pytorch_lightning.__doc__.replace(os.linesep + ' ', '') +# fp.write(m2r.convert(intro)) +# # fp.write(pytorch_lightning.__doc__) + +# # export the READme +# with open(os.path.join(PATH_ROOT, 'README.md'), 'r') as fp: +# readme = fp.read() +# # replace all paths to relative +# for ndir in (os.path.basename(p) for p in glob.glob(os.path.join(PATH_ROOT, '*')) +# if os.path.isdir(p)): +# readme = readme.replace('](%s/' % ndir, '](%s/%s/' % (PATH_ROOT, ndir)) +# with open('readme.md', 'w') as fp: +# fp.write(readme) + +for md in glob.glob(os.path.join(PATH_ROOT, '.github', '*.md')): + shutil.copy(md, os.path.join(PATH_HERE, os.path.basename(md))) + +# -- Project information ----------------------------------------------------- + +project = 'rising' +copyright = rising.__copyright__ +author = rising.__author__ + +# The short X.Y version +version = rising.__version__ +# The full version, including alpha/beta/rc tags +release = rising.__version__ + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. + +needs_sphinx = '2.0' + +# 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', + # 'sphinxcontrib.mockautodoc', # raises error: directive 'automodule' is already registered ... + # 'sphinxcontrib.fulltoc', # breaks pytorch-theme with unexpected kw argument 'titles_only' + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.linkcode', + 'sphinx.ext.autosummary', + 'sphinx.ext.napoleon', + 'recommonmark', + 'sphinx.ext.autosectionlabel', + # 'm2r', + 'nbsphinx', + 'sphinx_autodoc_typehints', + 'sphinx_paramlinks', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# https://berkeley-stat159-f17.github.io/stat159-f17/lectures/14-sphinx..html#conf.py-(cont.) +# https://stackoverflow.com/questions/38526888/embed-ipython-notebook-in-sphinx-document +# I execute the notebooks manually in advance. If notebooks test the code, +# they should be run at build time. +nbsphinx_execute = 'always' +nbsphinx_allow_errors = True +nbsphinx_requirejs_path = '' + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +# source_suffix = ['.rst', '.md', '.ipynb'] +source_suffix = { + '.rst': 'restructuredtext', + '.txt': 'markdown', + '.md': 'markdown', + '.ipynb': 'nbsphinx', +} + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ +] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# http://www.sphinx-doc.org/en/master/usage/theming.html#builtin-themes +# html_theme = 'bizstyle' +# https://sphinx-themes.org +html_theme = 'pytorch_sphinx_theme' +html_theme_path = [pytorch_sphinx_theme.get_html_theme_path()] + +# 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 = { + 'pytorch_project': rising.__homepage__, + 'canonical_url': rising.__homepage__, + 'collapse_navigation': False, + 'display_version': True, + 'logo_only': False, +} + +html_logo = 'images/logos/lightning_logo-name.svg' + +# 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 = ['images', '_templates'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = project + '-doc' + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', + + # Latex figure (float) alignment + 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, project + '.tex', project + ' Documentation', author, 'manual'), +] + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, project, project + ' Documentation', [author], 1) +] + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, project, project + ' Documentation', author, project, + 'One line description of project.', 'Miscellaneous'), +] + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), + 'torch': ('https://pytorch.org/docs/stable/', None), + 'numpy': ('https://docs.scipy.org/doc/numpy/', None), + 'PIL': ('https://pillow.readthedocs.io/en/stable/', None), + 'dill': ('https://dill.rtfd.io/', None), +} + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + +# https://github.com/rtfd/readthedocs.org/issues/1139 +# I use sphinx-apidoc to auto-generate API documentation for my project. +# Right now I have to commit these auto-generated files to my repository +# so that RTD can build them into HTML docs. It'd be cool if RTD could run +# sphinx-apidoc for me, since it's easy to forget to regen API docs +# and commit them to my repo after making changes to my code. + +PACKAGES = [ + rising.__name__, +] + + +def run_apidoc(_): + for pkg in PACKAGES: + argv = ['-e', '-o', PATH_HERE, os.path.join(PATH_HERE, PATH_ROOT, pkg), + '**/test_*', '--force', '--private', '--module-first'] + try: + # Sphinx 1.7+ + from sphinx.ext import apidoc + apidoc.main(argv) + except ImportError: + # Sphinx 1.6 (and earlier) + from sphinx import apidoc + argv.insert(0, apidoc.__file__) + apidoc.main(argv) + + +def setup(app): + app.connect('builder-inited', run_apidoc) + + +# copy all notebooks to local folder +path_nbs = os.path.join(PATH_HERE, 'notebooks') +if not os.path.isdir(path_nbs): + os.mkdir(path_nbs) +for path_ipynb in glob.glob(os.path.join(PATH_ROOT, 'notebooks', '*.ipynb')): + path_ipynb2 = os.path.join(path_nbs, os.path.basename(path_ipynb)) + shutil.copy(path_ipynb, path_ipynb2) + +# Ignoring Third-party packages +# https://stackoverflow.com/questions/15889621/sphinx-how-to-exclude-imports-in-automodule + +MOCK_REQUIRE_PACKAGES = [] +with open(os.path.join(PATH_ROOT, 'requirements.txt'), 'r') as fp: + for ln in fp.readlines(): + found = [ln.index(ch) for ch in list(',=<>#') if ch in ln] + pkg = ln[:min(found)] if found else ln + if pkg.rstrip(): + MOCK_REQUIRE_PACKAGES.append(pkg.rstrip()) + +# TODO: better parse from package since the import name and package name may differ +MOCK_MANUAL_PACKAGES = [ + 'torch', + 'torchvision', + 'numpy', + 'dill' +] +autodoc_mock_imports = MOCK_REQUIRE_PACKAGES + MOCK_MANUAL_PACKAGES +# for mod_name in MOCK_REQUIRE_PACKAGES: +# sys.modules[mod_name] = mock.Mock() + + +# Options for the linkcode extension +# ---------------------------------- +github_user = 'PhoenixDL' +github_repo = project + + +# Resolve function +# This function is used to populate the (source) links in the API +def linkcode_resolve(domain, info): + def find_source(): + # try to find the file and line number, based on code from numpy: + # https://github.com/numpy/numpy/blob/master/doc/source/conf.py#L286 + obj = sys.modules[info['module']] + for part in info['fullname'].split('.'): + obj = getattr(obj, part) + fname = inspect.getsourcefile(obj) + # https://github.com/rtfd/readthedocs.org/issues/5735 + if any([s in fname for s in ('readthedocs', 'rtfd', 'checkouts')]): + # /home/docs/checkouts/readthedocs.org/user_builds/pytorch_lightning/checkouts/ + # devel/pytorch_lightning/utilities/cls_experiment.py#L26-L176 + path_top = os.path.abspath(os.path.join('..', '..', '..')) + fname = os.path.relpath(fname, start=path_top) + else: + # Local build, imitate master + fname = 'master/' + os.path.relpath(fname, start=os.path.abspath('..')) + source, lineno = inspect.getsourcelines(obj) + return fname, lineno, lineno + len(source) - 1 + + if domain != 'py' or not info['module']: + return None + try: + filename = '%s#L%d-L%d' % find_source() + except Exception: + filename = info['module'].replace('.', '/') + '.py' + # import subprocess + # tag = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE, + # universal_newlines=True).communicate()[0][:-1] + branch = filename.split('/')[0] + # do mapping from latest tags to master + branch = {'latest': 'master', 'stable': 'master'}.get(branch, branch) + filename = '/'.join([branch] + filename.split('/')[1:]) + return "https://github.com/%s/%s/blob/%s" \ + % (github_user, github_repo, filename) + + +autodoc_member_order = 'groupwise' +autoclass_content = 'both' +# the options are fixed and will be soon in release, +# see https://github.com/sphinx-doc/sphinx/issues/5459 +autodoc_default_options = { + 'members': None, + 'methods': None, + # 'attributes': None, + 'special-members': '__call__', + 'exclude-members': '_abc_impl', + 'show-inheritance': True, + 'private-members': True, + 'noindex': True, +} + +# Sphinx will add “permalinks” for each heading and description environment as paragraph signs that +# become visible when the mouse hovers over them. +# This value determines the text for the permalink; it defaults to "¶". Set it to None or the empty +# string to disable permalinks. +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_add_permalinks +html_add_permalinks = "¶" + +# True to prefix each section label with the name of the document it is in, followed by a colon. +# For example, index:Introduction for a section called Introduction that appears in document index.rst. +# Useful for avoiding ambiguity when the same section heading appears in different documents. +# http://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html +autosectionlabel_prefix_document = True From e6e3d5f71e97c8d51ed99e87166c304b79a669a5 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:46:26 +0200 Subject: [PATCH 09/46] populate init --- rising/__init__.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/rising/__init__.py b/rising/__init__.py index 70264e5d..a88b6d5a 100644 --- a/rising/__init__.py +++ b/rising/__init__.py @@ -1,5 +1,29 @@ from ._version import get_versions + __version__ = get_versions()['version'] del get_versions -from rising.interface import AbstractMixin +__author__ = 'Justus Schock, Michael Baumgartner' +__author_email__ = 'justus.schock@rwth-aachen.de' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2019-2020, %s.' % __author__ +__homepage__ = 'https://github.com/PhoenixDL/rising' +# this has to be simple string, see: https://github.com/pypa/twine/issues/522 +__docs__ = "rising is a highly performant, PyTorch only framework for " \ + "efficient data augmentation with support for volumetric data" +__long_docs__ = "" + +try: + # This variable is injected in the __builtins__ by the build + # process. It used to enable importing subpackages of skimage when + # the binaries are not built + __RISING_SETUP__ +except NameError: + __RISING_SETUP__ = False + +if __RISING_SETUP__: + import sys # pragma: no-cover + sys.stdout.write(f'Partial import of `{__name__}` during the build process.\n') # pragma: no-cover + # We are not importing the rest of the lightning during the build process, as it may not be compiled yet +else: + from rising.interface import AbstractMixin From a00d0c87b9ab7d446c95a849de21003edd665997 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:46:52 +0200 Subject: [PATCH 10/46] update setup.py --- setup.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 84be1b7e..10e6d7d6 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,13 @@ import versioneer +try: + import builtins +except ImportError: + import __builtin__ as builtins + +builtins.__RISING_SETUP__ = True + def resolve_requirements(file): requirements = [] @@ -29,14 +36,17 @@ def read_file(file): requirements_async = resolve_requirements( os.path.join(os.path.dirname(__file__), "requirements", 'install_async.txt')) -readme = read_file(os.path.join(os.path.dirname(__file__), "README.md")) +readme = read_file(os.path.join(os.path.dirname(__file__), "README.md") + ).replace('.svg', '.png') + +import rising setup( name='rising', version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), packages=find_packages(), - url='https://github.com/phoenixdl/rising', + url=rising.__homepage__, test_suite="unittest", long_description=readme, long_description_content_type='text/markdown', @@ -44,8 +54,19 @@ def read_file(file): extras_require={'async': requirements_async}, tests_require=["coverage"], python_requires=">=3.6", - author="PhoenixDL", - maintainer='Michael Baumgartner, Justus Schock', - maintainer_email='justus.schock@rwth-aachen.de', - license='MIT', + author=rising.__author__, + author_email=rising.__author_email__, + include_package_data=True, + zip_safe=False, + keywords=['deep learning', 'augmentation', 'transforms', 'pytorch', 'medical'], + license=rising.__license__, + project_urls={ + "Bug Tracker": "https://github.com/PhoenixDL/rising/issues", + "Documentation": "https://rising.rtfd.io/en/latest/", + "Source Code": "https://github.com/PhoenixDL/rising", + }, + # TODO: Populate classifiers + classifiers = [ + + ] ) From 93bc974e9ebbc068256942f7a4ed333c45fb4633 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:47:16 +0200 Subject: [PATCH 11/46] adjust logo path --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9695903e..1e7b83f6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![logo](docs/source/images/rising_logo.svg "rising") +![logo](docs/source/images/logo/rising_logo.svg "rising") ![Project Status](https://img.shields.io/badge/status-alpha-red) ![PyPI](https://img.shields.io/pypi/v/rising) From eeafdf037e3c15934c8cd7708b1487293c55d8b3 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:58:02 +0200 Subject: [PATCH 12/46] jinja variables --- docs/source/_templates/theme_variables.jinja | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 docs/source/_templates/theme_variables.jinja diff --git a/docs/source/_templates/theme_variables.jinja b/docs/source/_templates/theme_variables.jinja new file mode 100644 index 00000000..888097fa --- /dev/null +++ b/docs/source/_templates/theme_variables.jinja @@ -0,0 +1,13 @@ +{%- set external_urls = { + 'github': 'https://github.com/PhoenixDL/rising', + 'github_issues': 'https://github.com/PhoenixDl/rising/issues', + 'contributing': 'https://github.com/PhoenixDl/rising/blob/master/CONTRIBUTING.md', + 'docs': 'https://rising.rtfd.io/en/latest', + 'discuss': 'https://phoenixdl.slack.com', + 'previous_pytorch_versions': 'https://rising.rtfd.io/en/latest/', + 'home': 'https://rising.rtfd.io/en/latest/', + 'get_started': 'https://rising.readthedocs.io/en/latest/get_started.html', + 'features': 'https://rising.rtfd.io/en/latest/', + 'support': 'https://github.com/PhoenixDl/rising/issues', +} +-%} \ No newline at end of file From 3e36a977048da4b2957855661a25e79f3bc01fa9 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 21:11:25 +0200 Subject: [PATCH 13/46] add index and getting started --- docs/source/get_started.rst | 0 docs/source/index.rst | 48 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 docs/source/get_started.rst create mode 100644 docs/source/index.rst diff --git a/docs/source/get_started.rst b/docs/source/get_started.rst new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..5c138f47 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,48 @@ +rising Documentation +==================== + +.. toctree:: + :maxdepth: 1 + :name: start + :caption: Start Here + + + get_started + +.. toctree:: + :maxdepth: 2 + :name: docs + :caption: Python API + + + loading + ops + transforms + utils + interface + +.. toctree:: + :maxdepth: 1 + :name: Tutorials & Examples + :caption: Tutorials + + + Using external transformations + An Overview on rising transformsations + +.. toctree:: + :maxdepth: 1 + :name: community + :caption: Community + + + CODE_OF_CONDUCT.md + CONTRIBUTING.md + PULL_REQUEST_TEMPLATE.md + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` \ No newline at end of file From 3d8b7ad9c1221653a00c576cc3c5e2d630197411 Mon Sep 17 00:00:00 2001 From: Michael Baumgartner Date: Fri, 1 May 2020 19:12:20 +0000 Subject: [PATCH 14/46] autopep8 fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 10e6d7d6..f9f613e0 100644 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ def read_file(file): "Source Code": "https://github.com/PhoenixDL/rising", }, # TODO: Populate classifiers - classifiers = [ + classifiers=[ ] ) From f8a6fa1cbfcce90dd1d23e5a51e0cd0dad48636a Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 21:42:55 +0200 Subject: [PATCH 15/46] update conf --- docs/source/conf.py | 22 ++++++++++++++-------- docs/source/get_started.rst | 2 ++ docs/source/index.rst | 13 +++++++------ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 79d7ea36..1bccc31d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,18 +12,17 @@ # 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. -import os -import sys import glob -import shutil import inspect +import os +import shutil +import sys # import m2r -import builtins import pytorch_sphinx_theme PATH_HERE = os.path.abspath(os.path.dirname(__file__)) -PATH_ROOT = os.path.join(PATH_HERE, '..', '..') +PATH_ROOT = os.path.dirname(os.path.dirname(PATH_HERE)) sys.path.insert(0, os.path.abspath(PATH_ROOT)) import rising # noqa: E402 @@ -152,7 +151,7 @@ 'logo_only': False, } -html_logo = 'images/logos/lightning_logo-name.svg' +html_logo = 'images/logo/rising_logo.svg' # 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, @@ -242,7 +241,7 @@ 'torch': ('https://pytorch.org/docs/stable/', None), 'numpy': ('https://docs.scipy.org/doc/numpy/', None), 'PIL': ('https://pillow.readthedocs.io/en/stable/', None), - 'dill': ('https://dill.rtfd.io/', None), + 'dill': ('https://dill.rtfd.io/en/stable', None), } # -- Options for todo extension ---------------------------------------------- @@ -293,7 +292,14 @@ def setup(app): # https://stackoverflow.com/questions/15889621/sphinx-how-to-exclude-imports-in-automodule MOCK_REQUIRE_PACKAGES = [] -with open(os.path.join(PATH_ROOT, 'requirements.txt'), 'r') as fp: +with open(os.path.join(PATH_ROOT, 'requirements', 'install.txt'), 'r') as fp: + for ln in fp.readlines(): + found = [ln.index(ch) for ch in list(',=<>#') if ch in ln] + pkg = ln[:min(found)] if found else ln + if pkg.rstrip(): + MOCK_REQUIRE_PACKAGES.append(pkg.rstrip()) + +with open(os.path.join(PATH_ROOT, 'requirements', 'install_async.txt'), 'r') as fp: for ln in fp.readlines(): found = [ln.index(ch) for ch in list(',=<>#') if ch in ln] pkg = ln[:min(found)] if found else ln diff --git a/docs/source/get_started.rst b/docs/source/get_started.rst index e69de29b..c3ce665d 100644 --- a/docs/source/get_started.rst +++ b/docs/source/get_started.rst @@ -0,0 +1,2 @@ +Getting Started +=============== \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index 5c138f47..b329ceec 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,15 +6,12 @@ rising Documentation :name: start :caption: Start Here - get_started - .. toctree:: :maxdepth: 2 :name: docs :caption: Python API - loading ops transforms @@ -26,7 +23,6 @@ rising Documentation :name: Tutorials & Examples :caption: Tutorials - Using external transformations An Overview on rising transformsations @@ -35,7 +31,6 @@ rising Documentation :name: community :caption: Community - CODE_OF_CONDUCT.md CONTRIBUTING.md PULL_REQUEST_TEMPLATE.md @@ -45,4 +40,10 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` -* :ref:`search` \ No newline at end of file +* :ref:`search` + +.. This is here to make sphinx aware of the modules but not throw an error/warning +.. toctree:: + :hidden: + + rising._version From 09c445b7840ff71f9fbb206e77101f60428da398 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 19:57:52 +0200 Subject: [PATCH 16/46] Add docs makefile --- docs/Makefile | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/Makefile diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..69fe55ec --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SOURCEDIR = source +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) \ No newline at end of file From 8bd47860169ba1eca04aca370bc6edbde12e0012 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 19:58:32 +0200 Subject: [PATCH 17/46] add windows make file --- docs/make.bat | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/make.bat diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..8710393c --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd \ No newline at end of file From 1f30d42f1aca7c5f755f5d80e12b1ce198d6315c Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:01:24 +0200 Subject: [PATCH 18/46] add requirements --- docs/requirements.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..e3a0a12e --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,12 @@ +sphinx>=2.0, <3.0 +recommonmark # fails with badges +m2r # fails with multi-line text +nbsphinx +pandoc +docutils +sphinxcontrib-fulltoc +sphinxcontrib-mockautodoc +git+https://github.com/pytorch/pytorch_sphinx_theme.git +# pip_shims +sphinx-autodoc-typehints +sphinx-paramlinks<0.4.0 \ No newline at end of file From a788c4eff2e2246cb1f1b3c0fbcd9d5b190f6938 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:01:35 +0200 Subject: [PATCH 19/46] add build script --- docs/build_docs.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 docs/build_docs.sh diff --git a/docs/build_docs.sh b/docs/build_docs.sh new file mode 100644 index 00000000..609f4a8e --- /dev/null +++ b/docs/build_docs.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +make clean ; make html --debug --jobs 2 SPHINXOPTS="-W" \ No newline at end of file From ed3df7b370e54a33630c60f2023247b81abcdc60 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:01:46 +0200 Subject: [PATCH 20/46] move images --- .../images/rising_logo.png} | Bin docs/{_static => source/images}/rising_logo.svg | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/{_static/rising_Logo.png => source/images/rising_logo.png} (100%) rename docs/{_static => source/images}/rising_logo.svg (100%) diff --git a/docs/_static/rising_Logo.png b/docs/source/images/rising_logo.png similarity index 100% rename from docs/_static/rising_Logo.png rename to docs/source/images/rising_logo.png diff --git a/docs/_static/rising_logo.svg b/docs/source/images/rising_logo.svg similarity index 100% rename from docs/_static/rising_logo.svg rename to docs/source/images/rising_logo.svg From 63e92de06af3c5e302828149922ac45fb3b907bc Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:03:03 +0200 Subject: [PATCH 21/46] adjust image path in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04f6fb57..a0be14fb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![logo](docs/_static/rising_logo.svg "rising") +![logo](docs/source/images/rising_logo.svg "rising") ![Project Status](https://img.shields.io/badge/status-alpha-red) ![PyPI](https://img.shields.io/pypi/v/rising) From 11ed678c541625f353df0fbaade6ad15388dfce1 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:40:56 +0200 Subject: [PATCH 22/46] move image path --- docs/source/images/{ => logo}/rising_logo.png | Bin docs/source/images/{ => logo}/rising_logo.svg | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/source/images/{ => logo}/rising_logo.png (100%) rename docs/source/images/{ => logo}/rising_logo.svg (100%) diff --git a/docs/source/images/rising_logo.png b/docs/source/images/logo/rising_logo.png similarity index 100% rename from docs/source/images/rising_logo.png rename to docs/source/images/logo/rising_logo.png diff --git a/docs/source/images/rising_logo.svg b/docs/source/images/logo/rising_logo.svg similarity index 100% rename from docs/source/images/rising_logo.svg rename to docs/source/images/logo/rising_logo.svg From 0a083f2d431ca8854302d18a578387701ac858e4 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:46:02 +0200 Subject: [PATCH 23/46] initial doc config --- docs/source/conf.py | 386 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 docs/source/conf.py diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..79d7ea36 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,386 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# 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. + +import os +import sys +import glob +import shutil +import inspect + +# import m2r +import builtins +import pytorch_sphinx_theme + +PATH_HERE = os.path.abspath(os.path.dirname(__file__)) +PATH_ROOT = os.path.join(PATH_HERE, '..', '..') +sys.path.insert(0, os.path.abspath(PATH_ROOT)) + +import rising # noqa: E402 + +# -- Project documents ------------------------------------------------------- + +# # export the documentation +# with open('intro.rst', 'w') as fp: +# intro = pytorch_lightning.__doc__.replace(os.linesep + ' ', '') +# fp.write(m2r.convert(intro)) +# # fp.write(pytorch_lightning.__doc__) + +# # export the READme +# with open(os.path.join(PATH_ROOT, 'README.md'), 'r') as fp: +# readme = fp.read() +# # replace all paths to relative +# for ndir in (os.path.basename(p) for p in glob.glob(os.path.join(PATH_ROOT, '*')) +# if os.path.isdir(p)): +# readme = readme.replace('](%s/' % ndir, '](%s/%s/' % (PATH_ROOT, ndir)) +# with open('readme.md', 'w') as fp: +# fp.write(readme) + +for md in glob.glob(os.path.join(PATH_ROOT, '.github', '*.md')): + shutil.copy(md, os.path.join(PATH_HERE, os.path.basename(md))) + +# -- Project information ----------------------------------------------------- + +project = 'rising' +copyright = rising.__copyright__ +author = rising.__author__ + +# The short X.Y version +version = rising.__version__ +# The full version, including alpha/beta/rc tags +release = rising.__version__ + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. + +needs_sphinx = '2.0' + +# 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', + # 'sphinxcontrib.mockautodoc', # raises error: directive 'automodule' is already registered ... + # 'sphinxcontrib.fulltoc', # breaks pytorch-theme with unexpected kw argument 'titles_only' + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.linkcode', + 'sphinx.ext.autosummary', + 'sphinx.ext.napoleon', + 'recommonmark', + 'sphinx.ext.autosectionlabel', + # 'm2r', + 'nbsphinx', + 'sphinx_autodoc_typehints', + 'sphinx_paramlinks', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# https://berkeley-stat159-f17.github.io/stat159-f17/lectures/14-sphinx..html#conf.py-(cont.) +# https://stackoverflow.com/questions/38526888/embed-ipython-notebook-in-sphinx-document +# I execute the notebooks manually in advance. If notebooks test the code, +# they should be run at build time. +nbsphinx_execute = 'always' +nbsphinx_allow_errors = True +nbsphinx_requirejs_path = '' + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +# source_suffix = ['.rst', '.md', '.ipynb'] +source_suffix = { + '.rst': 'restructuredtext', + '.txt': 'markdown', + '.md': 'markdown', + '.ipynb': 'nbsphinx', +} + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ +] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# http://www.sphinx-doc.org/en/master/usage/theming.html#builtin-themes +# html_theme = 'bizstyle' +# https://sphinx-themes.org +html_theme = 'pytorch_sphinx_theme' +html_theme_path = [pytorch_sphinx_theme.get_html_theme_path()] + +# 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 = { + 'pytorch_project': rising.__homepage__, + 'canonical_url': rising.__homepage__, + 'collapse_navigation': False, + 'display_version': True, + 'logo_only': False, +} + +html_logo = 'images/logos/lightning_logo-name.svg' + +# 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 = ['images', '_templates'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = project + '-doc' + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', + + # Latex figure (float) alignment + 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, project + '.tex', project + ' Documentation', author, 'manual'), +] + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, project, project + ' Documentation', [author], 1) +] + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, project, project + ' Documentation', author, project, + 'One line description of project.', 'Miscellaneous'), +] + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), + 'torch': ('https://pytorch.org/docs/stable/', None), + 'numpy': ('https://docs.scipy.org/doc/numpy/', None), + 'PIL': ('https://pillow.readthedocs.io/en/stable/', None), + 'dill': ('https://dill.rtfd.io/', None), +} + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + +# https://github.com/rtfd/readthedocs.org/issues/1139 +# I use sphinx-apidoc to auto-generate API documentation for my project. +# Right now I have to commit these auto-generated files to my repository +# so that RTD can build them into HTML docs. It'd be cool if RTD could run +# sphinx-apidoc for me, since it's easy to forget to regen API docs +# and commit them to my repo after making changes to my code. + +PACKAGES = [ + rising.__name__, +] + + +def run_apidoc(_): + for pkg in PACKAGES: + argv = ['-e', '-o', PATH_HERE, os.path.join(PATH_HERE, PATH_ROOT, pkg), + '**/test_*', '--force', '--private', '--module-first'] + try: + # Sphinx 1.7+ + from sphinx.ext import apidoc + apidoc.main(argv) + except ImportError: + # Sphinx 1.6 (and earlier) + from sphinx import apidoc + argv.insert(0, apidoc.__file__) + apidoc.main(argv) + + +def setup(app): + app.connect('builder-inited', run_apidoc) + + +# copy all notebooks to local folder +path_nbs = os.path.join(PATH_HERE, 'notebooks') +if not os.path.isdir(path_nbs): + os.mkdir(path_nbs) +for path_ipynb in glob.glob(os.path.join(PATH_ROOT, 'notebooks', '*.ipynb')): + path_ipynb2 = os.path.join(path_nbs, os.path.basename(path_ipynb)) + shutil.copy(path_ipynb, path_ipynb2) + +# Ignoring Third-party packages +# https://stackoverflow.com/questions/15889621/sphinx-how-to-exclude-imports-in-automodule + +MOCK_REQUIRE_PACKAGES = [] +with open(os.path.join(PATH_ROOT, 'requirements.txt'), 'r') as fp: + for ln in fp.readlines(): + found = [ln.index(ch) for ch in list(',=<>#') if ch in ln] + pkg = ln[:min(found)] if found else ln + if pkg.rstrip(): + MOCK_REQUIRE_PACKAGES.append(pkg.rstrip()) + +# TODO: better parse from package since the import name and package name may differ +MOCK_MANUAL_PACKAGES = [ + 'torch', + 'torchvision', + 'numpy', + 'dill' +] +autodoc_mock_imports = MOCK_REQUIRE_PACKAGES + MOCK_MANUAL_PACKAGES +# for mod_name in MOCK_REQUIRE_PACKAGES: +# sys.modules[mod_name] = mock.Mock() + + +# Options for the linkcode extension +# ---------------------------------- +github_user = 'PhoenixDL' +github_repo = project + + +# Resolve function +# This function is used to populate the (source) links in the API +def linkcode_resolve(domain, info): + def find_source(): + # try to find the file and line number, based on code from numpy: + # https://github.com/numpy/numpy/blob/master/doc/source/conf.py#L286 + obj = sys.modules[info['module']] + for part in info['fullname'].split('.'): + obj = getattr(obj, part) + fname = inspect.getsourcefile(obj) + # https://github.com/rtfd/readthedocs.org/issues/5735 + if any([s in fname for s in ('readthedocs', 'rtfd', 'checkouts')]): + # /home/docs/checkouts/readthedocs.org/user_builds/pytorch_lightning/checkouts/ + # devel/pytorch_lightning/utilities/cls_experiment.py#L26-L176 + path_top = os.path.abspath(os.path.join('..', '..', '..')) + fname = os.path.relpath(fname, start=path_top) + else: + # Local build, imitate master + fname = 'master/' + os.path.relpath(fname, start=os.path.abspath('..')) + source, lineno = inspect.getsourcelines(obj) + return fname, lineno, lineno + len(source) - 1 + + if domain != 'py' or not info['module']: + return None + try: + filename = '%s#L%d-L%d' % find_source() + except Exception: + filename = info['module'].replace('.', '/') + '.py' + # import subprocess + # tag = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE, + # universal_newlines=True).communicate()[0][:-1] + branch = filename.split('/')[0] + # do mapping from latest tags to master + branch = {'latest': 'master', 'stable': 'master'}.get(branch, branch) + filename = '/'.join([branch] + filename.split('/')[1:]) + return "https://github.com/%s/%s/blob/%s" \ + % (github_user, github_repo, filename) + + +autodoc_member_order = 'groupwise' +autoclass_content = 'both' +# the options are fixed and will be soon in release, +# see https://github.com/sphinx-doc/sphinx/issues/5459 +autodoc_default_options = { + 'members': None, + 'methods': None, + # 'attributes': None, + 'special-members': '__call__', + 'exclude-members': '_abc_impl', + 'show-inheritance': True, + 'private-members': True, + 'noindex': True, +} + +# Sphinx will add “permalinks” for each heading and description environment as paragraph signs that +# become visible when the mouse hovers over them. +# This value determines the text for the permalink; it defaults to "¶". Set it to None or the empty +# string to disable permalinks. +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_add_permalinks +html_add_permalinks = "¶" + +# True to prefix each section label with the name of the document it is in, followed by a colon. +# For example, index:Introduction for a section called Introduction that appears in document index.rst. +# Useful for avoiding ambiguity when the same section heading appears in different documents. +# http://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html +autosectionlabel_prefix_document = True From c665971cf436d1f1ac2945465ea25afb38a08190 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:46:26 +0200 Subject: [PATCH 24/46] populate init --- rising/__init__.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/rising/__init__.py b/rising/__init__.py index 70264e5d..a88b6d5a 100644 --- a/rising/__init__.py +++ b/rising/__init__.py @@ -1,5 +1,29 @@ from ._version import get_versions + __version__ = get_versions()['version'] del get_versions -from rising.interface import AbstractMixin +__author__ = 'Justus Schock, Michael Baumgartner' +__author_email__ = 'justus.schock@rwth-aachen.de' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2019-2020, %s.' % __author__ +__homepage__ = 'https://github.com/PhoenixDL/rising' +# this has to be simple string, see: https://github.com/pypa/twine/issues/522 +__docs__ = "rising is a highly performant, PyTorch only framework for " \ + "efficient data augmentation with support for volumetric data" +__long_docs__ = "" + +try: + # This variable is injected in the __builtins__ by the build + # process. It used to enable importing subpackages of skimage when + # the binaries are not built + __RISING_SETUP__ +except NameError: + __RISING_SETUP__ = False + +if __RISING_SETUP__: + import sys # pragma: no-cover + sys.stdout.write(f'Partial import of `{__name__}` during the build process.\n') # pragma: no-cover + # We are not importing the rest of the lightning during the build process, as it may not be compiled yet +else: + from rising.interface import AbstractMixin From ee44c035103511a9e3b9b1b765e7b5efe11398f7 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:46:52 +0200 Subject: [PATCH 25/46] update setup.py --- setup.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 84be1b7e..10e6d7d6 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,13 @@ import versioneer +try: + import builtins +except ImportError: + import __builtin__ as builtins + +builtins.__RISING_SETUP__ = True + def resolve_requirements(file): requirements = [] @@ -29,14 +36,17 @@ def read_file(file): requirements_async = resolve_requirements( os.path.join(os.path.dirname(__file__), "requirements", 'install_async.txt')) -readme = read_file(os.path.join(os.path.dirname(__file__), "README.md")) +readme = read_file(os.path.join(os.path.dirname(__file__), "README.md") + ).replace('.svg', '.png') + +import rising setup( name='rising', version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), packages=find_packages(), - url='https://github.com/phoenixdl/rising', + url=rising.__homepage__, test_suite="unittest", long_description=readme, long_description_content_type='text/markdown', @@ -44,8 +54,19 @@ def read_file(file): extras_require={'async': requirements_async}, tests_require=["coverage"], python_requires=">=3.6", - author="PhoenixDL", - maintainer='Michael Baumgartner, Justus Schock', - maintainer_email='justus.schock@rwth-aachen.de', - license='MIT', + author=rising.__author__, + author_email=rising.__author_email__, + include_package_data=True, + zip_safe=False, + keywords=['deep learning', 'augmentation', 'transforms', 'pytorch', 'medical'], + license=rising.__license__, + project_urls={ + "Bug Tracker": "https://github.com/PhoenixDL/rising/issues", + "Documentation": "https://rising.rtfd.io/en/latest/", + "Source Code": "https://github.com/PhoenixDL/rising", + }, + # TODO: Populate classifiers + classifiers = [ + + ] ) From 0bb63795cd2a46d2c4b85ff5669c6cacb7ce9d1f Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:47:16 +0200 Subject: [PATCH 26/46] adjust logo path --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0be14fb..1518fd99 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![logo](docs/source/images/rising_logo.svg "rising") +![logo](docs/source/images/logo/rising_logo.svg "rising") ![Project Status](https://img.shields.io/badge/status-alpha-red) ![PyPI](https://img.shields.io/pypi/v/rising) From 2b6e662374b2b5d593644360195b18d63b7b4ac1 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 20:58:02 +0200 Subject: [PATCH 27/46] jinja variables --- docs/source/_templates/theme_variables.jinja | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 docs/source/_templates/theme_variables.jinja diff --git a/docs/source/_templates/theme_variables.jinja b/docs/source/_templates/theme_variables.jinja new file mode 100644 index 00000000..888097fa --- /dev/null +++ b/docs/source/_templates/theme_variables.jinja @@ -0,0 +1,13 @@ +{%- set external_urls = { + 'github': 'https://github.com/PhoenixDL/rising', + 'github_issues': 'https://github.com/PhoenixDl/rising/issues', + 'contributing': 'https://github.com/PhoenixDl/rising/blob/master/CONTRIBUTING.md', + 'docs': 'https://rising.rtfd.io/en/latest', + 'discuss': 'https://phoenixdl.slack.com', + 'previous_pytorch_versions': 'https://rising.rtfd.io/en/latest/', + 'home': 'https://rising.rtfd.io/en/latest/', + 'get_started': 'https://rising.readthedocs.io/en/latest/get_started.html', + 'features': 'https://rising.rtfd.io/en/latest/', + 'support': 'https://github.com/PhoenixDl/rising/issues', +} +-%} \ No newline at end of file From 668557072ad55a1b6f6c97a617e81c081381d742 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 21:11:25 +0200 Subject: [PATCH 28/46] add index and getting started --- docs/source/get_started.rst | 0 docs/source/index.rst | 48 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 docs/source/get_started.rst create mode 100644 docs/source/index.rst diff --git a/docs/source/get_started.rst b/docs/source/get_started.rst new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..5c138f47 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,48 @@ +rising Documentation +==================== + +.. toctree:: + :maxdepth: 1 + :name: start + :caption: Start Here + + + get_started + +.. toctree:: + :maxdepth: 2 + :name: docs + :caption: Python API + + + loading + ops + transforms + utils + interface + +.. toctree:: + :maxdepth: 1 + :name: Tutorials & Examples + :caption: Tutorials + + + Using external transformations + An Overview on rising transformsations + +.. toctree:: + :maxdepth: 1 + :name: community + :caption: Community + + + CODE_OF_CONDUCT.md + CONTRIBUTING.md + PULL_REQUEST_TEMPLATE.md + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` \ No newline at end of file From 83a3c85b7171aa25769b5405374db183544a6f05 Mon Sep 17 00:00:00 2001 From: Michael Baumgartner Date: Fri, 1 May 2020 19:12:20 +0000 Subject: [PATCH 29/46] autopep8 fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 10e6d7d6..f9f613e0 100644 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ def read_file(file): "Source Code": "https://github.com/PhoenixDL/rising", }, # TODO: Populate classifiers - classifiers = [ + classifiers=[ ] ) From c68f3a60454c43f5a8802425a26afeab86cf2ae7 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Fri, 1 May 2020 21:42:55 +0200 Subject: [PATCH 30/46] update conf --- docs/source/conf.py | 22 ++++++++++++++-------- docs/source/get_started.rst | 2 ++ docs/source/index.rst | 13 +++++++------ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 79d7ea36..1bccc31d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,18 +12,17 @@ # 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. -import os -import sys import glob -import shutil import inspect +import os +import shutil +import sys # import m2r -import builtins import pytorch_sphinx_theme PATH_HERE = os.path.abspath(os.path.dirname(__file__)) -PATH_ROOT = os.path.join(PATH_HERE, '..', '..') +PATH_ROOT = os.path.dirname(os.path.dirname(PATH_HERE)) sys.path.insert(0, os.path.abspath(PATH_ROOT)) import rising # noqa: E402 @@ -152,7 +151,7 @@ 'logo_only': False, } -html_logo = 'images/logos/lightning_logo-name.svg' +html_logo = 'images/logo/rising_logo.svg' # 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, @@ -242,7 +241,7 @@ 'torch': ('https://pytorch.org/docs/stable/', None), 'numpy': ('https://docs.scipy.org/doc/numpy/', None), 'PIL': ('https://pillow.readthedocs.io/en/stable/', None), - 'dill': ('https://dill.rtfd.io/', None), + 'dill': ('https://dill.rtfd.io/en/stable', None), } # -- Options for todo extension ---------------------------------------------- @@ -293,7 +292,14 @@ def setup(app): # https://stackoverflow.com/questions/15889621/sphinx-how-to-exclude-imports-in-automodule MOCK_REQUIRE_PACKAGES = [] -with open(os.path.join(PATH_ROOT, 'requirements.txt'), 'r') as fp: +with open(os.path.join(PATH_ROOT, 'requirements', 'install.txt'), 'r') as fp: + for ln in fp.readlines(): + found = [ln.index(ch) for ch in list(',=<>#') if ch in ln] + pkg = ln[:min(found)] if found else ln + if pkg.rstrip(): + MOCK_REQUIRE_PACKAGES.append(pkg.rstrip()) + +with open(os.path.join(PATH_ROOT, 'requirements', 'install_async.txt'), 'r') as fp: for ln in fp.readlines(): found = [ln.index(ch) for ch in list(',=<>#') if ch in ln] pkg = ln[:min(found)] if found else ln diff --git a/docs/source/get_started.rst b/docs/source/get_started.rst index e69de29b..c3ce665d 100644 --- a/docs/source/get_started.rst +++ b/docs/source/get_started.rst @@ -0,0 +1,2 @@ +Getting Started +=============== \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index 5c138f47..b329ceec 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,15 +6,12 @@ rising Documentation :name: start :caption: Start Here - get_started - .. toctree:: :maxdepth: 2 :name: docs :caption: Python API - loading ops transforms @@ -26,7 +23,6 @@ rising Documentation :name: Tutorials & Examples :caption: Tutorials - Using external transformations An Overview on rising transformsations @@ -35,7 +31,6 @@ rising Documentation :name: community :caption: Community - CODE_OF_CONDUCT.md CONTRIBUTING.md PULL_REQUEST_TEMPLATE.md @@ -45,4 +40,10 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` -* :ref:`search` \ No newline at end of file +* :ref:`search` + +.. This is here to make sphinx aware of the modules but not throw an error/warning +.. toctree:: + :hidden: + + rising._version From b59e149095e97b1d0dede3a4cbc57a33848781dc Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 16:22:18 +0200 Subject: [PATCH 31/46] add read the docs config --- .readthedocs.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .readthedocs.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..d2e3e018 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,24 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# Build documentation with MkDocs +#mkdocs: +# configuration: mkdocs.yml + +# Optionally build your docs in additional formats such as PDF and ePub +formats: all + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.7 + install: + - requirements: docs/requirements.txt + #- requirements: requirements.txt \ No newline at end of file From c7456a0d071406eed0216bd9fb0b5fa7f7cfca5d Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 16:52:28 +0200 Subject: [PATCH 32/46] update collate docstr. --- rising/loading/collate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rising/loading/collate.py b/rising/loading/collate.py index 094be43c..b40a0dd5 100644 --- a/rising/loading/collate.py +++ b/rising/loading/collate.py @@ -1,12 +1,11 @@ -import numpy as np -import torch import collections.abc from typing import Any +import numpy as np +import torch __all__ = ["numpy_collate", "do_nothing_collate"] - default_collate_err_msg_format = ( "default_collate: batch must contain tensors, numpy arrays, numbers, " "dicts or lists; found {}") @@ -17,6 +16,7 @@ def numpy_collate(batch: Any) -> Any: function to collate the samples to a whole batch of numpy arrays. PyTorch Tensors, scalar values and sequences will be casted to arrays automatically. + Args: batch: a batch of samples. In most cases either sequence, mapping or mixture of them From 2f69324c03f9e4c912b153922750dce2b95dff0d Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 16:53:49 +0200 Subject: [PATCH 33/46] update doc config --- docs/source/conf.py | 22 +++++++---------- .../{get_started.rst => getting_started.rst} | 0 docs/source/index.rst | 24 +++++++++---------- 3 files changed, 20 insertions(+), 26 deletions(-) rename docs/source/{get_started.rst => getting_started.rst} (100%) diff --git a/docs/source/conf.py b/docs/source/conf.py index 1bccc31d..c69ac1f4 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -47,6 +47,8 @@ for md in glob.glob(os.path.join(PATH_ROOT, '.github', '*.md')): shutil.copy(md, os.path.join(PATH_HERE, os.path.basename(md))) +for md in ['CONTRIBUTING.md']: + shutil.copy(os.path.join(PATH_ROOT, md), os.path.join(PATH_HERE, md.lower())) # -- Project information ----------------------------------------------------- @@ -90,14 +92,6 @@ # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] -# https://berkeley-stat159-f17.github.io/stat159-f17/lectures/14-sphinx..html#conf.py-(cont.) -# https://stackoverflow.com/questions/38526888/embed-ipython-notebook-in-sphinx-document -# I execute the notebooks manually in advance. If notebooks test the code, -# they should be run at build time. -nbsphinx_execute = 'always' -nbsphinx_allow_errors = True -nbsphinx_requirejs_path = '' - # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # @@ -281,12 +275,12 @@ def setup(app): # copy all notebooks to local folder -path_nbs = os.path.join(PATH_HERE, 'notebooks') -if not os.path.isdir(path_nbs): - os.mkdir(path_nbs) -for path_ipynb in glob.glob(os.path.join(PATH_ROOT, 'notebooks', '*.ipynb')): - path_ipynb2 = os.path.join(path_nbs, os.path.basename(path_ipynb)) - shutil.copy(path_ipynb, path_ipynb2) +# path_nbs = os.path.join(PATH_HERE, 'notebooks') +# if not os.path.isdir(path_nbs): +# os.mkdir(path_nbs) +# for path_ipynb in glob.glob(os.path.join(PATH_ROOT, 'notebooks', '*.ipynb')): +# path_ipynb2 = os.path.join(path_nbs, os.path.basename(path_ipynb)) +# shutil.copy(path_ipynb, path_ipynb2) # Ignoring Third-party packages # https://stackoverflow.com/questions/15889621/sphinx-how-to-exclude-imports-in-automodule diff --git a/docs/source/get_started.rst b/docs/source/getting_started.rst similarity index 100% rename from docs/source/get_started.rst rename to docs/source/getting_started.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index b329ceec..5b37b95a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -2,21 +2,22 @@ rising Documentation ==================== .. toctree:: - :maxdepth: 1 - :name: start - :caption: Start Here + :maxdepth: 2 + :name: introduction + :caption: Getting Started + + getting_started - get_started .. toctree:: :maxdepth: 2 :name: docs :caption: Python API - loading - ops - transforms - utils - interface + rising.loading + rising.ops + rising.transforms + rising.utils + rising.interface .. toctree:: :maxdepth: 1 @@ -31,9 +32,8 @@ rising Documentation :name: community :caption: Community - CODE_OF_CONDUCT.md - CONTRIBUTING.md - PULL_REQUEST_TEMPLATE.md + contributing.md + pull_request_template.md Indices and tables ------------------ From 9290c13bc9a1a97901867e70fa7e16d1ae5c4203 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 18:11:29 +0200 Subject: [PATCH 34/46] Add forst working doc config --- docs/source/loading.rst | 0 docs/source/ops.rst.py | 0 docs/source/transforms.rst | 0 docs/source/utils.rst | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/source/loading.rst create mode 100644 docs/source/ops.rst.py create mode 100644 docs/source/transforms.rst create mode 100644 docs/source/utils.rst diff --git a/docs/source/loading.rst b/docs/source/loading.rst new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/ops.rst.py b/docs/source/ops.rst.py new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/utils.rst b/docs/source/utils.rst new file mode 100644 index 00000000..e69de29b From ac5727a82b14822fe28eed8e3d6f59f955650d9a Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 18:12:12 +0200 Subject: [PATCH 35/46] update docstrings to be sphinx compatible --- rising/loading/dataset.py | 11 +- rising/loading/loader.py | 12 +-- rising/ops/tensor.py | 2 +- rising/transforms/abstract.py | 6 +- rising/transforms/affine.py | 133 ++++++++++++------------ rising/transforms/channel.py | 2 +- rising/transforms/compose.py | 5 +- rising/transforms/functional/affine.py | 66 ++++++------ rising/transforms/functional/channel.py | 2 +- rising/transforms/functional/spatial.py | 4 +- rising/transforms/functional/tensor.py | 10 +- rising/transforms/functional/utility.py | 8 +- rising/transforms/intensity.py | 22 ++-- rising/transforms/spatial.py | 22 ++-- rising/transforms/tensor.py | 4 +- rising/transforms/utility.py | 17 +-- rising/utils/affine.py | 18 ++-- 17 files changed, 171 insertions(+), 173 deletions(-) diff --git a/rising/loading/dataset.py b/rising/loading/dataset.py index e13bbdc5..d0fb125d 100644 --- a/rising/loading/dataset.py +++ b/rising/loading/dataset.py @@ -114,7 +114,7 @@ def __init__(self, data_path: the path(s) containing the actual data samples load_fn: function to load the actual data mode: whether to append the sample to a list or to extend the list - by it. Supported modes are: :param:`append` and :param:`extend`. + by it. Supported modes are: ``append`` and ``extend``. Default: ``append`` num_workers: the number of workers to use for preloading. ``0`` means, all the data will be loaded in the main process, @@ -122,12 +122,12 @@ def __init__(self, the number of logical cores. verbose: whether to show the loading progress. **load_kwargs: additional keyword arguments. - Passed directly to :param:`load_fn` + Passed directly to :attr:`load_fn` Warnings: if using multiprocessing to load data, there are some restrictions to which :func:`load_fn` are supported, please refer to the - :module:`dill` or :module:`pickle` documentation + :mod:`dill` or :mod:`pickle` documentation """ super().__init__() @@ -224,14 +224,13 @@ def update(*a): def _add_item(data: list, item: Any, mode: str) -> None: """ Adds items to the given data list. The actual way of adding these - items depends on :param:`mode` + items depends on :attr:`mode` Args: data: the list containing the already loaded data item: the current item which will be added to the list mode: the string specifying the mode of how the item should be - added. - + added.F Raises: TypeError: No known mode detected """ diff --git a/rising/loading/loader.py b/rising/loading/loader.py index 5e4601a7..1610c2a0 100644 --- a/rising/loading/loader.py +++ b/rising/loading/loader.py @@ -55,8 +55,7 @@ class DataLoader(_DataLoader): used, whose :meth:`__len__` is not implemented, because the actual length depends on both the iterable as well as multi-process loading configurations. So one should not query this method unless - they work with a map-style dataset. See `Dataset Types`_ for more - details on these two types of datasets. + they work with a map-style dataset. Warnings: If the ``spawn`` start method is used, :attr:`worker_init_fn` @@ -99,12 +98,12 @@ def __init__(self, dataset: Union[Sequence, Dataset], batch. Usually this accepts either mappings or sequences and returns the same type containing transformed elements gpu_transforms: transforms which can be applied to a whole batch - (on the GPU). Unlike :param:`batch_transforms` this is not + (on the GPU). Unlike :attr:`batch_transforms` this is not done in multiple processes, but in the main process on the GPU, because GPUs are capable of non-blocking and asynchronous working. Before executing these transforms all data will be - moved to :param:`device`. This copy is done in a non-blocking - way if :param:`pin_memory` is set to True. + moved to :attr:`device`. This copy is done in a non-blocking + way if :attr:`pin_memory` is set to True. device: the device to move the data to for gpu_transforms. If None: the device will be the current device. sampler: defines the strategy to draw samples from @@ -372,8 +371,9 @@ def __next__(self) -> Any: class _SingleProcessDataLoaderIter(__SingleProcessDataLoaderIter): """Iterator over Dataloader. - This iterator adds functionality for per-sample transforms + This iterator adds functionality for per-sample transforms outside the dataset and per-batch transforms on both, CPU and GPU. + """ def __init__(self, loader: DataLoader): diff --git a/rising/ops/tensor.py b/rising/ops/tensor.py index 20af81fd..54dbb0a1 100644 --- a/rising/ops/tensor.py +++ b/rising/ops/tensor.py @@ -10,7 +10,7 @@ def torch_one_hot(target: torch.Tensor, num_classes: Optional[int] = None) -> to Args: target: tensor to be converted - num_classes: number of classes. If :param:`num_classes` is None, + num_classes: number of classes. If :attr:`num_classes` is None, the maximum of target is used Returns: diff --git a/rising/transforms/abstract.py b/rising/transforms/abstract.py index 011e0368..cfe561bf 100644 --- a/rising/transforms/abstract.py +++ b/rising/transforms/abstract.py @@ -97,7 +97,7 @@ def forward(self, **data) -> dict: class PerSampleTransform(BaseTransform): """ Apply transformation to each sample in batch individually - :param:`augment_fn` must be callable with option :param:`out` + :attr:`augment_fn` must be callable with option :attr:`out` where results are saved in """ @@ -203,7 +203,7 @@ def forward(self, **data) -> dict: class RandomProcess(AbstractMixin): """ Saves specified function to generate random values to current class. - Function is saved inside :param:`random_fn`. + Function is saved inside :attr:`random_fn`. """ def __init__(self, *args, random_mode: str, @@ -222,7 +222,7 @@ def __init__(self, *args, random_mode: str, random_module: module from where function random function should be imported rand_seq: if enabled, multiple random values are generated - if :param:`random_args` is of type Sequence[Sequence] + if :attr:`random_args` is of type Sequence[Sequence] """ super().__init__(*args, **kwargs) self.random_module = random_module diff --git a/rising/transforms/affine.py b/rising/transforms/affine.py index 5d841f22..0722eabb 100644 --- a/rising/transforms/affine.py +++ b/rising/transforms/affine.py @@ -39,8 +39,8 @@ def __init__(self, **kwargs): """ Args: - matrix: if given, overwrites the parameters for :param:`scale`, - :param:rotation` and :param:`translation`. + matrix: if given, overwrites the parameters for :attr:`scale`, + :attr:rotation` and :attr:`translation`. Should be a matrix of shape [(BATCHSIZE,) NDIM, NDIM(+1)] This matrix represents the whole transformation matrix keys: keys which should be augmented @@ -318,32 +318,32 @@ def __init__(self, Args: scale: the scale factor(s). Supported are: * a single parameter (as float or int), which will be - replicated for all dimensions and batch samples + replicated for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension * None will be treated as a scaling factor of 1 rotation: the rotation factor(s). The rotation is performed in consecutive order axis0 -> axis1 (-> axis 2). Supported are: * a single parameter (as float or int), which will be - replicated for all dimensions and batch samples + replicated for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension - * None will be treated as a rotation factor of 1 + * None will be treated as a rotation angle of 0 translation : torch.Tensor, int, float the translation offset(s) relative to image (should be in the range [0, 1]). Supported are: * a single parameter (as float or int), which will be - replicated for all dimensions and batch samples + replicated for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension * None will be treated as a translation offset of 0 keys: keys which should be augmented @@ -438,13 +438,13 @@ def __init__(self, rotation: the rotation factor(s). The rotation is performed in consecutive order axis0 -> axis1 (-> axis 2). Supported are: * a single parameter (as float or int), which will be - replicated for all dimensions and batch samples + replicated for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension - * None will be treated as a rotation factor of 1 + * ``None`` will be treated as a rotation angle of 0 keys: keys which should be augmented grad: enable gradient computation inside transformation degree: whether the given rotation(s) are in degrees. @@ -515,11 +515,11 @@ def __init__(self, the translation offset(s) relative to image (should be in the range [0, 1]). Supported are: * a single parameter (as float or int), which will be - replicated for all dimensions and batch samples + replicated for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension * None will be treated as a translation offset of 0 keys: keys which should be augmented @@ -583,6 +583,11 @@ def assemble_matrix(self, **data) -> torch.Tensor: class Scale(BaseAffine): + """Class Performing a Scale-Only Affine Transformation on a given + sample dict. + The transformation will be applied to all the dict-entries specified + in :attr:`keys`. + """ def __init__(self, scale: AffineParamType, keys: Sequence = ('data',), @@ -595,56 +600,50 @@ def __init__(self, reverse_order: bool = False, **kwargs): """ - Class Performing a Scale-Only Affine Transformation on a given - sample dict. - The transformation will be applied to all the dict-entries specified - in :attr:`keys`. - - Parameters - ---------- - scale : torch.Tensor, int, float, optional - the scale factor(s). Supported are: - * a single parameter (as float or int), which will be replicated - for all dimensions and batch samples + Args: + scale : torch.Tensor, int, float, optional + the scale factor(s). Supported are: + * a single parameter (as float or int), which will be + replicated for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions - * a parameter per dimension, which will be replicated for all - batch samples + replicated for all dimensions + * a parameter per dimension, which will be replicated for + all batch samples * a parameter per sampler per dimension - None will be treated as a scaling factor of 1 - keys: Sequence - keys which should be augmented - grad: bool - enable gradient computation inside transformation - degree : bool - whether the given rotation(s) are in degrees. - Only valid for rotation parameters, which aren't passed as full - transformation matrix. - output_size : Iterable - if given, this will be the resulting image size. - Defaults to ``None`` - adjust_size : bool - if True, the resulting image size will be calculated dynamically - to ensure that the whole image fits. - interpolation_mode : str - interpolation mode to calculate output values - 'bilinear' | 'nearest'. Default: 'bilinear' - padding_mode : - padding mode for outside grid values - 'zeros' | 'border' | 'reflection'. Default: 'zeros' - align_corners : bool - Geometrically, we consider the pixels of the input as - squares rather than points. If set to True, the extrema (-1 and 1) - are considered as referring to the center points of the input’s - corner pixels. If set to False, they are instead considered as - referring to the corner points of the input’s corner pixels, - making the sampling more resolution agnostic. - reverse_order: bool - reverses the coordinate order of the transformation to conform - to the pytorch convention: transformation params order [W,H(,D)] and - batch order [(D,)H,W] - **kwargs : - additional keyword arguments passed to the affine transform + * None will be treated as a scaling factor of 1 + keys: Sequence + keys which should be augmented + grad: bool + enable gradient computation inside transformation + degree : bool + whether the given rotation(s) are in degrees. + Only valid for rotation parameters, which aren't passed as full + transformation matrix. + output_size : Iterable + if given, this will be the resulting image size. + Defaults to ``None`` + adjust_size : bool + if True, the resulting image size will be calculated + dynamically to ensure that the whole image fits. + interpolation_mode : str + interpolation mode to calculate output values + 'bilinear' | 'nearest'. Default: 'bilinear' + padding_mode : + padding mode for outside grid values + 'zeros' | 'border' | 'reflection'. Default: 'zeros' + align_corners : bool + Geometrically, we consider the pixels of the input as + squares rather than points. If set to True, the extrema + (-1 and 1) are considered as referring to the center points of + the input’s corner pixels. If set to False, they are instead + considered as referring to the corner points of the input’s + corner pixels, making the sampling more resolution agnostic. + reverse_order: bool + reverses the coordinate order of the transformation to conform + to the pytorch convention: transformation params order + [W,H(,D)] and batch order [(D,)H,W] + **kwargs : + additional keyword arguments passed to the affine transform """ super().__init__(scale=scale, rotation=None, diff --git a/rising/transforms/channel.py b/rising/transforms/channel.py index b51ed123..f3138503 100644 --- a/rising/transforms/channel.py +++ b/rising/transforms/channel.py @@ -19,7 +19,7 @@ def __init__(self, num_classes: int, keys: Sequence = ('seg',), """ Args: - num_classes: number of classes. If :param:`num_classes` is None, + num_classes: number of classes. If :attr:`num_classes` is None, the number of classes is automatically determined from the current batch (by using the max of the current batch and assuming a consecutive order from zero) diff --git a/rising/transforms/compose.py b/rising/transforms/compose.py index e6925cdd..cd678a3d 100644 --- a/rising/transforms/compose.py +++ b/rising/transforms/compose.py @@ -56,13 +56,14 @@ class Compose(AbstractTransform): def __init__(self, *transforms, shuffle: bool = False, transform_call: Callable[[Any, Callable], Any] = dict_call): """ - Args + Args: transforms: one or multiple transformations which are applied in consecutive order shuffle: apply transforms in random order transform_call: function which determines how transforms are called. By default Mappings and Sequences are unpacked during the transform. + """ super().__init__(grad=True) if isinstance(transforms[0], Sequence): @@ -168,7 +169,7 @@ def __init__(self, *transforms, dropout: Union[float, Sequence[float]] = 0.5, onsecutive order dropout: if provided as float, each transform is skipped with the given probability - if :param:`dropout` is a sequence, it needs to specify the + if :attr:`dropout` is a sequence, it needs to specify the dropout probability for each given transform random_mode: specifies distribution which should be used to sample additive value diff --git a/rising/transforms/functional/affine.py b/rising/transforms/functional/affine.py index 4dac4050..0952bebd 100644 --- a/rising/transforms/functional/affine.py +++ b/rising/transforms/functional/affine.py @@ -63,24 +63,24 @@ def create_scale(scale: AffineParamType, Formats the given scale parameters to a homogeneous transformation matrix Args: - scale : the scale factor(s). Supported are: - * a single parameter (as float or int), which will be replicated + scale : the scale factor(s). Supported are: + * a single parameter (as float or int), which will be replicated for all dimensions and batch samples - * a parameter per sample, which will be + * a parameter per sample, which will be replicated for all dimensions - * a parameter per dimension, which will be replicated for all + * a parameter per dimension, which will be replicated for all batch samples - * a parameter per sampler per dimension - * None will be treated as a scaling factor of 1 - batchsize: the number of samples per batch - ndim: the dimensionality of the transform - device: the device to put the resulting tensor to. - Defaults to the torch default device - dtype: the dtype of the resulting trensor. - Defaults to the torch default dtype - image_transform: inverts the scale matrix to match expected behavior - when applied to an image, e.g. scale>1 increases the size of an - image but decrease the size of an grid + * a parameter per sampler per dimension + * None will be treated as a scaling factor of 1 + batchsize: the number of samples per batch + ndim: the dimensionality of the transform + device: the device to put the resulting tensor to. + Defaults to the torch default device + dtype: the dtype of the resulting trensor. + Defaults to the torch default dtype + image_transform: inverts the scale matrix to match expected behavior + when applied to an image, e.g. scale>1 increases the size of an + image but decrease the size of an grid Returns: the homogeneous transformation matrix [N, NDIM + 1, NDIM + 1], N is @@ -111,11 +111,11 @@ def create_translation(offset: AffineParamType, Args: offset: the translation offset(s). Supported are: * a single parameter (as float or int), which will be replicated - for all dimensions and batch samples + for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension * None will be treated as a translation offset of 0 batchsize: the number of samples per batch @@ -157,13 +157,13 @@ def create_rotation(rotation: AffineParamType, Args: rotation: the rotation factor(s). Supported are: * a single parameter (as float or int), which will be replicated - for all dimensions and batch samples + for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension - * None will be treated as a rotation factor of 0 + * None will be treated as a rotation angle of 0 batchsize: the number of samples per batch ndim : the dimensionality of the transform degree: whether the given rotation(s) are in degrees. @@ -294,29 +294,29 @@ def parametrize_matrix(scale: AffineParamType, Args: scale: the scale factor(s). Supported are: * a single parameter (as float or int), which will be replicated - for all dimensions and batch samples + for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension * None will be treated as a scaling factor of 1 rotation: the rotation factor(s). Supported are: * a single parameter (as float or int), which will be replicated - for all dimensions and batch samples + for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension * None will be treated as a rotation factor of 1 translation: the translation offset(s). Supported are: * a single parameter (as float or int), which will be replicated - for all dimensions and batch samples + for all dimensions and batch samples * a parameter per sample, which will be - replicated for all dimensions + replicated for all dimensions * a parameter per dimension, which will be replicated for all - batch samples + batch samples * a parameter per sampler per dimension * None will be treated as a translation offset of 0 batchsize: the number of samples per batch @@ -360,7 +360,7 @@ def affine_point_transform(point_batch: torch.Tensor, ``NDIM`` is the number of spatial dimensions matrix_batch : torch.Tensor a batch of affine matrices with shape [N, NDIM, NDIM + 1], - N is the batch size and NDIM is the number of spatial dimensions + N is the batch size and NDIM is the number of spatial dimensions Returns: the batch of transformed points in cartesian coordinates) @@ -414,7 +414,7 @@ def affine_image_transform(image_batch: torch.Tensor, resolutions (that is, after being upsampled or downsampled). Notes: - :param:`output_size` and :param:`adjust_size` are mutually exclusive. + :attr:`output_size` and :attr:`adjust_size` are mutually exclusive. If None of them is set, the resulting image will have the same size as the input image. """ diff --git a/rising/transforms/functional/channel.py b/rising/transforms/functional/channel.py index eaa532b5..40cab2e9 100644 --- a/rising/transforms/functional/channel.py +++ b/rising/transforms/functional/channel.py @@ -14,7 +14,7 @@ def one_hot_batch(target: torch.Tensor, num_classes: Optional[int] = None) -> to Args: target: tensor to be converted num_classes: number of classes. - If :param:`num_classes` is None, the maximum of target is used + If :attr:`num_classes` is None, the maximum of target is used Returns: one hot encoded tensor diff --git a/rising/transforms/functional/spatial.py b/rising/transforms/functional/spatial.py index 4468ee61..ce679dff 100644 --- a/rising/transforms/functional/spatial.py +++ b/rising/transforms/functional/spatial.py @@ -46,8 +46,8 @@ def resize(data: torch.Tensor, mode: str = 'nearest', align_corners: Optional[bool] = None, preserve_range: bool = False): """ - Down/up-sample sample to either the given :param:`size` or the given - :param:`scale_factor` + Down/up-sample sample to either the given :attr:`size` or the given + :attr:`scale_factor` The modes available for resizing are: nearest, linear (3D-only), bilinear, bicubic (4D-only), trilinear (5D-only), area diff --git a/rising/transforms/functional/tensor.py b/rising/transforms/functional/tensor.py index 33ac0e6e..8110e8e6 100644 --- a/rising/transforms/functional/tensor.py +++ b/rising/transforms/functional/tensor.py @@ -13,11 +13,11 @@ def tensor_op(data: data_type, fn: str, *args, **kwargs) -> data_type: Invokes a function form a tensor Args: - data: data which should be pushed to device. Sequence and mapping items - are mapping individually to gpu - fn: tensor function - *args: positional arguments passed to tensor function - **kwargs: keyword arguments passed to tensor function + data: data which should be pushed to device. Sequence and mapping items + are mapping individually to gpu + fn: tensor function + *args: positional arguments passed to tensor function + **kwargs: keyword arguments passed to tensor function Returns: data which was pushed to device diff --git a/rising/transforms/functional/utility.py b/rising/transforms/functional/utility.py index 7e69cf2a..b6026644 100644 --- a/rising/transforms/functional/utility.py +++ b/rising/transforms/functional/utility.py @@ -18,11 +18,11 @@ def box_to_seg(boxes: Sequence[Sequence[int]], (dim0_min, dim1_min, dim0_max, dim1_max, [dim2_min, dim2_max]). Supported bounding boxes for 2D (4 entries per box) and 3d (6 entries per box) - shape: if :param:`out` is not provided, shape of output tensor must + shape: if :attr:`out` is not provided, shape of output tensor must be specified - dtype: if :param:`out` is not provided, + dtype: if :attr:`out` is not provided, dtype of output tensor must be specified - device: if :param:`out` is not provided, + device: if :attr:`out` is not provided, device of output tensor must be specified out: if not None, the segmentation will be saved inside this tensor @@ -82,7 +82,7 @@ def instance_to_semantic(instance: torch.Tensor, semantic segmentation Warnings: - :param:`instance` needs to encode objects starting from 1 and the + :attr:`instance` needs to encode objects starting from 1 and the indices need to be continuous (0 is interpreted as background) """ seg = torch.zeros_like(instance) diff --git a/rising/transforms/intensity.py b/rising/transforms/intensity.py index fb62b6f4..d522ca98 100644 --- a/rising/transforms/intensity.py +++ b/rising/transforms/intensity.py @@ -22,11 +22,11 @@ def __init__(self, min: float, max: float, keys: Sequence = ('data',), grad: bool = False, **kwargs): """ Args: - min: minimal value - max: maximal value - keys: the keys corresponding to the values to clamp - grad: enable gradient computation inside transformation - **kwargs: keyword arguments passed to augment_fn + min: minimal value + max: maximal value + keys: the keys corresponding to the values to clamp + grad: enable gradient computation inside transformation + **kwargs: keyword arguments passed to augment_fn """ super().__init__(augment_fn=torch.clamp, keys=keys, grad=grad, min=min, max=max, **kwargs) @@ -165,11 +165,11 @@ def __init__(self, gamma: Union[float, Sequence] = (0.5, 2), gamma: if gamma is float it is always applied. if gamma is a sequence it is interpreted as the minimal and maximal value. If the maximal value is greater than one, - the transform chooses gamma <1 in 50% of the cases and - gamma >1 in the other cases. - keys: keys to normalize - grad: enable gradient computation inside transformation - **kwargs: keyword arguments passed to superclass + the transform chooses gamma < 1 in 50% of the cases and + gamma > 1 in the other cases. + keys: keys to normalize + grad: enable gradient computation inside transformation + **kwargs: keyword arguments passed to superclass """ super().__init__(augment_fn=gamma_correction, keys=keys, grad=grad) self.kwargs = kwargs @@ -207,7 +207,7 @@ def forward(self, **data) -> dict: class RandomValuePerChannel(RandomProcess, PerChannelTransform): """ Apply augmentations which take random values as input by keyword - :param:`value` + :attr:`value` """ def __init__(self, augment_fn: callable, random_mode: str, random_args: Sequence = (), diff --git a/rising/transforms/spatial.py b/rising/transforms/spatial.py index e78c52c1..1f79801e 100644 --- a/rising/transforms/spatial.py +++ b/rising/transforms/spatial.py @@ -121,7 +121,7 @@ def __init__(self, size: Union[int, Sequence[int]], mode: str = 'nearest', class Zoom(RandomProcess, BaseTransform): """Apply augment_fn to keys. By default the scaling factor is sampled from a uniform distribution with the range specified by - :param:`random_args` + :attr:`random_args` """ def __init__(self, random_args: Union[Sequence, Sequence[Sequence]] = (0.75, 1.25), @@ -181,16 +181,16 @@ def __init__(self, scheduler: scheduler_type, mode: str = 'nearest', scheduler: scheduler which determined the current size. The scheduler is called with the current iteration of the transform - mode: one of ``nearest``, ``linear``, ``bilinear``, ``bicubic``, - ``trilinear``, ``area`` (for more inforamtion see - :func:`torch.nn.functional.interpolate`) - align_corners: input and output tensors are aligned by the center - points of their corners pixels, preserving the values at the - corner pixels. - preserve_range: output tensor has same range as input tensor - keys: keys which should be augmented - grad: enable gradient computation inside transformation - **kwargs: keyword arguments passed to augment_fn + mode: one of ``nearest``, ``linear``, ``bilinear``, ``bicubic``, + ``trilinear``, ``area`` (for more inforamtion see + :func:`torch.nn.functional.interpolate`) + align_corners: input and output tensors are aligned by the center + points of their corners pixels, preserving the values at the + corner pixels. + preserve_range: output tensor has same range as input tensor + keys: keys which should be augmented + grad: enable gradient computation inside transformation + **kwargs: keyword arguments passed to augment_fn Warnings: When this transformations is used in combination with diff --git a/rising/transforms/tensor.py b/rising/transforms/tensor.py index dbf67361..16d78298 100644 --- a/rising/transforms/tensor.py +++ b/rising/transforms/tensor.py @@ -16,8 +16,8 @@ def __init__(self, keys: Sequence = ('data',), grad: bool = False, **kwargs): """ Args: keys: keys which should be transformed - grad: enable gradient computation inside transformation - **kwargs: keyword arguments passed to augment_fn + grad: enable gradient computation inside transformation + **kwargs: keyword arguments passed to augment_fn """ super().__init__(augment_fn=default_convert, keys=keys, grad=grad, **kwargs) diff --git a/rising/transforms/utility.py b/rising/transforms/utility.py index d0cadaa9..a6e6c8d8 100644 --- a/rising/transforms/utility.py +++ b/rising/transforms/utility.py @@ -67,14 +67,15 @@ def __init__(self, keys: Mapping[Hashable, Hashable], shape: Sequence[int], dtype: torch.dtype, device: Union[torch.device, str], grad: bool = False, **kwargs): """ - keys: the key specifies which item to use as the bounding boxes and - the item specifies where the save the bounding boxes - shape: spatial shape of output tensor (batchsize is derived from - bounding boxes and has one channel) - dtype: dtype of segmentation - device: device of segmentation - grad: enable gradient computation inside transformation - **kwargs: Additional keyword arguments forwarded to the Base Class + Args: + keys: the key specifies which item to use as the bounding boxes and + the item specifies where the save the bounding boxes + shape: spatial shape of output tensor (batchsize is derived from + bounding boxes and has one channel) + dtype: dtype of segmentation + device: device of segmentation + grad: enable gradient computation inside transformation + **kwargs: Additional keyword arguments forwarded to the Base Class """ super().__init__(grad=grad, **kwargs) self.keys = keys diff --git a/rising/utils/affine.py b/rising/utils/affine.py index 125b08fb..7a5fa51d 100644 --- a/rising/utils/affine.py +++ b/rising/utils/affine.py @@ -54,11 +54,11 @@ def matrix_to_cartesian(batch: torch.Tensor, keep_square: bool = False coordinates. Args: - batch: the batch oif matrices to convert back - keep_square: if False: returns a NDIM x NDIM+1 matrix to keep the - translation part - if True: returns a NDIM x NDIM matrix but looses the translation part - defaults to False. + batch: the batch oif matrices to convert back + keep_square: if False: returns a NDIM x NDIM+1 matrix to keep the + translation part + if True: returns a NDIM x NDIM matrix but looses the translation + part. defaults to False. Returns: the given matrix in cartesian coordinates @@ -122,9 +122,7 @@ def get_batched_eye(batchsize: int, ndim: int, dtype : torch.dtype, str, optional the dtype of the resulting trensor. Defaults to the default dtype - Returns - ------- - torch.Tensor + Returns: batched eye matrix """ @@ -152,8 +150,8 @@ def unit_box(n: int, scale: Optional[torch.Tensor] = None) -> torch.Tensor: Create a sclaed version of a unit box Args: - n: number of dimensions - scale: scaling of each dimension + n: number of dimensions + scale: scaling of each dimension Returns: scaled unit box From 69e6c127dc465f2a8c5eb087b32675fe5e284d20 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 18:14:24 +0200 Subject: [PATCH 36/46] update config --- docs/source/conf.py | 10 +++++----- docs/source/index.rst | 13 ++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index c69ac1f4..6c356c92 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -19,7 +19,7 @@ import sys # import m2r -import pytorch_sphinx_theme +import rising_sphinx_theme PATH_HERE = os.path.abspath(os.path.dirname(__file__)) PATH_ROOT = os.path.dirname(os.path.dirname(PATH_HERE)) @@ -45,8 +45,6 @@ # with open('readme.md', 'w') as fp: # fp.write(readme) -for md in glob.glob(os.path.join(PATH_ROOT, '.github', '*.md')): - shutil.copy(md, os.path.join(PATH_HERE, os.path.basename(md))) for md in ['CONTRIBUTING.md']: shutil.copy(os.path.join(PATH_ROOT, md), os.path.join(PATH_HERE, md.lower())) @@ -130,8 +128,10 @@ # http://www.sphinx-doc.org/en/master/usage/theming.html#builtin-themes # html_theme = 'bizstyle' # https://sphinx-themes.org -html_theme = 'pytorch_sphinx_theme' -html_theme_path = [pytorch_sphinx_theme.get_html_theme_path()] +# html_theme = 'pytorch_sphinx_theme' +# html_theme_path = [pytorch_sphinx_theme.get_html_theme_path()] +html_theme = 'rising_sphinx_theme' +html_theme_path = [rising_sphinx_theme.get_html_theme_path()] # 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 diff --git a/docs/source/index.rst b/docs/source/index.rst index 5b37b95a..b5d6697f 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -13,11 +13,11 @@ rising Documentation :name: docs :caption: Python API - rising.loading - rising.ops - rising.transforms - rising.utils - rising.interface + loading + ops + transforms + utils + interface .. toctree:: :maxdepth: 1 @@ -33,7 +33,6 @@ rising Documentation :caption: Community contributing.md - pull_request_template.md Indices and tables ------------------ @@ -46,4 +45,4 @@ Indices and tables .. toctree:: :hidden: - rising._version + modules From 87b4fd6f65c1c668b05b0334119505aa02699459 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 19:48:28 +0200 Subject: [PATCH 37/46] update logos and requirements --- docs/requirements.txt | 2 +- docs/source/images/logo/rising_icon.svg | 26 +++++++++++++++++ docs/source/images/logo/rising_logo.png | Bin 45112 -> 45261 bytes docs/source/images/logo/rising_logo.svg | 36 ++++++++++-------------- 4 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 docs/source/images/logo/rising_icon.svg diff --git a/docs/requirements.txt b/docs/requirements.txt index e3a0a12e..070e72ba 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,7 +6,7 @@ pandoc docutils sphinxcontrib-fulltoc sphinxcontrib-mockautodoc -git+https://github.com/pytorch/pytorch_sphinx_theme.git +git+https://github.com/PhoenixDL/rising_sphinx_theme.git # pip_shims sphinx-autodoc-typehints sphinx-paramlinks<0.4.0 \ No newline at end of file diff --git a/docs/source/images/logo/rising_icon.svg b/docs/source/images/logo/rising_icon.svg new file mode 100644 index 00000000..3f5ff070 --- /dev/null +++ b/docs/source/images/logo/rising_icon.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/source/images/logo/rising_logo.png b/docs/source/images/logo/rising_logo.png index 1f0b970275a77a0338f04143328dcb1b50876ad2..490496e5633d3118d7da9bcb6872faa4f1aeb936 100644 GIT binary patch literal 45261 zcmeFZby$?|+BOQqAVW8bGzdrwB3%kdcMXyv4N54|4N7-N2$CY*9V3D?2+}Df-QD}1 z-&)^(*SGZjbN{zJhGWK2p68A$&g;C+`w3Q6mchlki-m%Mf-5I0rH+CEEk!{=je^|* z-!xYx8G<*|*XlA)P>TC0*T7$-%;0k7N=hiK;4=&b^}Q7e6!~Ak&t33?f`ax56$K5v zqawfi1o=Pz3N8JF_J2O3Mj`*>pryzlc%!tEQqctO5af5@Pbc`f|L=G3SanH^>gGp`51P`W?{3gZNDK_xp?4n83Sz_P{X$?kf)-MlpXh^ppy;|#T>?}! zD4}YCElPahy=YpZxYTz-d=zw0UbX0%kB|t&_`->I^Uxm`5K>qrwXF@?$80$k@{QK2WptQyr_&zAG zzrI$JQK0Mw{PHuLVnYUmK0Y}GzkRr$GW~kC&p5H3NUJwPKXz3XMY`)2rY|~<_ZO?iWI#b>d%mhx zngq+{O7{9_8L!qUPd)1op{_~F53m1=B}?eTBoHCk<|HQOkm4P43pT^R_QEAPE)B#M z&qKUn+`kxT95EOfB@0g_5$agvK|p%}8~rE(k3?3biil~L^e?|)LJvm8L9axV#54d7cm?SA-=X>B0e;cR{jT6-Vbz!N$e(Mk}M z{=k%=#e$@9h{qN^mSbGdg10#yD$=)XnUHAzQvHjwm68Eh8@09@=DP(+o8yludd`xw zFhWXLFcrGg;=*}r!=Cjo!Q(?BnAkxzhjnh0kMo#$`6JG(U>7Ipw)IPqvw@H5mXFFI ze=&#G1U^1y5pSh9c<=q1srYEC)NRFT7%0u|-jTc{{96CpB(2R~M)-gljPSe7LpY@j zWEQQVYqao*{y%GR@wv-+UzXA)IP)*TPht*)zO@EVV*?20Dc%mFd#JqQf|h+%oz9yQ5Sz%%zwMkj^4C9e7zandPOpVzkF{00F;%}x>`?BC{yI|ac5hnV zHzq95GBY8>#r(?_7Am7a5=Pun*1f4v=en}!@bh+Kn)3`P;_eM3q?as_@4B*>9a8Mm zYlnuk_1ua6%LEYJU9k+E4qpgCt`9zU4SvZ@{Sl`bHf__=!a1;u;Va>z&Wsn|lOkRC zi!*4}Am!V{{NIe9T^h_LCz&F?FU03&NYJrSUqRV8UndTDLO<&UTl#ycLY3KAHh<~D zr#W2}p5Njxck%yXHSj=SH3gNt>q(5LEnD=0^-cFV*z3Gf6U}CmJfrzM!`|pttA-V& z^YoCV`~I0ae5^py;CcBo(}W&+8PT(}nskrjeqD14hkCD5M|NKttwC;)7p9|~zJo#9 z>7UMs0_~Lt)6mr;JL*o5pq+T!uS21LEw>gbHc%ZVi;+EWB~@=rff07&YL&gYNA;KS ze|QJR|C5)Il?c~Y!Wv8Pf;W*YYG_3{IJcHPQpxDDt`e6oqN&Q!A;EPK`!Cz^02^$F zxs}|om z4E*ZCS46MpA2W2E+edl3AFJdqBQVASBTyU+%ahWELVyc+?B-V9iQD(0F+pXY?W%C% zn7Eq|Q!V^*@?wvVeEDxSWDdN_>Kz$5b219onZniXODn?r%T0!l_qtO`tzEos0&T;4 zWT(>4_jYUDA{cQu z@*Ty^z<1EU#OSAB1$sJ*Fs3T*Me#YsRj$TfzGcR$HPHP{wZqlu?#--Mfi-j_M?NJ^ z*F(G5hxwOD{5$v-nb{;9w+mR6b)0DnV`YC{i}-l*i^kMWO|IUMCCgA{)lqf^HSqDD ztN0FBQIBdL`ilyNC^3te%Fe~jsThbA%LHk3>5iw&adeXt>DqZtn2Yr79(v+i1nB;M z_~M-e;0fROsBi{kX+6JM3@t<)R56jrI+*vWJMVaQ;72#Te6;4qguj(pupC{FhU5Qd zfO{hcoRKpH{i(D9;%JNAO08qC0QKx+wPmQ{_+E3YOxNr@`Pw-X{(4@ISqvsxh~J+E z{YC{`Vmk$P%UlL=^iAK2yJN74^K2`zob8G8+(m!dy$*!|`LU^q77ldvqXvC-XT)=Y zKipd435Y84_q|A{*-*#2+lS~QvU;$WT0F$GiHY7e-?Wl12#v3kMW4TJ9&9D(VIsbD z=P&EAii`|1yy!T=2|gDqjHU%Tn(~d$cNoch!qP4i`=hh;s90{XdpK-zF>s{wy19S# z`ZGKT0@GfqoiBtE+uP|YS7$G%x8in0E-<>7%QZrW_T)WT$kb$0;D}G6c{<)xv zvS2kZDMmK6_H7L%tIrv;p%k-U2b|YGtPzymCUw{7;$$}E;ynKSNHzid3nr7$D>-E&X@KHt`t|H5+Shy#qpIv?c87c)A@vMjpP4e-NsLl-Z`FU zUKC@o0zd&P2jh=%J#D7B!7Fp=*Exp1l_U#Pw{oNll^=CE|8DzVvc#${SWD4Lh;3(r zkMqD=fGyYnYqDv08tQKFujlJ*pK!9tx*q+dR$K*l)-9(^+VvVBji5 zkEzVCh#)$#-m3uupt4Q0=5^_OfmOO^hZdY>TUd=f25AvKH;m>>Ni2F6o~U z><2!GVCPoY@O>jl^TQ5*-?sDwVts(l2XeKHlYAu*JD-O!{g*$K2ep^Ay|wF#Vw?5* zBL?_LcpzanzC|3pEW&%6WI}i}j`&$jNVNM^q#vX1PN@W~#21aHon>Z(Y1sQ^Q1ufC+D zbM`?0W2aFd>tK1+2S^0v33iQ|B_fKeaso-m_R)!Qo}I*1CN1XZPWf?vpKd9v#9(-%Xc!j5wOHUE?c7e-T-i&=;A>ONsTD*$;whhE()nYK9NM z74fN8&*ZR~tt!OTjnT_t#P(MLD)k(;Uw>^EGa9|CXl45&1fsz1GXr2DsBZd!%GZbO zV+=-RBAMcl*bQw1{caN!^(4O}I1#;^mhm95dG&R$x&zi!X(Z5`yO%Yt97Z&Y%-`8hMvTNsU zBAE;TsH7ZN9qkf`CI|G1yK!-^K3D3}dV^eH?7ifPyY=6@k;@2@Yo$^_WJuYV=K87c5WOrY7Fd2AtY`6% zRkk+RJe)|5i+v?-c#Jt9i2asQll;G}kpx+q-nn1ikRY+1{d{OaW?y(Fc+s4W@!#RF zfOaN=rSbA%Z2-yNDi2ez0x^v556T&%&evqE1{qxKEf%P-qYob=+>QAs^fcpM4a5!F zA!3zjlrY);JH{ZQfG3cbPaN94nG7o%GLhN$c}rrSv(XYmR`J8xg=<1jlx3!vW#`E6 zzEzo+|N4K21s_(hewxuzVmh#FPeHY3OZLY$(!PhoY_9V(o`kzsPEngL(_&uDEj^#1 zHCZc=dyf0xW^l(3gnvoiScfph^qVi0weucLEa>X7;TpQ3&(?)q12(q`6lz}YPjR73 zck_l0Nml*oQl=m-#bmsD&BGXVeRO*$^Ie6GybV?y_k+~@>%9)?a&j0 zb8}WpOiE$@y$2E!iC`Vp%5}pm;%wF^t+>ApW>EuV5w2qus<$cbolMC!7W2y4VlDxI zo@c4_`F|!u+9x0#!x$-vqkU}7o>&on8?53x<1z~RAl^L8g?iKedTI4I^UZ~@kqu-( z_==B<{SRxL0p)T|O74SPy6s1E(M5)8EU4cG&jk041Z;XkzOA!CFRMe5MbHR_ty5@J zmX7}?jM*Hvvw507u?0aV00-Jn>0P)K5lvp?6EPujn75B39(8(eXWQ!vsLuUUP1_T{T_4@Mc zi)zLb&0IxtxB0-^c{(MAye}tth2E@2ISNP7!F+t0VU&W70l6ya%1!Pkxsz2k8WZJa zk|$`*T)S8E^Y3YLu}E1})zyiPHpU`<_kUEd7+A>2Yko#Jie!3x+<-j|Kdh39NK-24 z(ryWeGk$KT6YlZ(rX0%5ro|JrNo$e*TiuiXo{1ACCv%hicll0L9fDGJ-;n~H_flU{ z1z!){T+Q7`Uv-D6QiJ&~3emyW)Zr0QL z?$*)2Li{NgNRz8rYQ(#?dMeeo7;^%!K+0VbcDT3Bfe-fY+sfR*{e(gr5Z+@j-bSr6 z04TVxKKJECg6VsCf1f1G+L>2w>t8;{DXD(T9Bn$8RS-Fo1~~5llkd;ZDjHw?(BtPQ zShWjf*Vfj)2S5t`}qd@nhHS&XLZ85Baz&xpT6H)b(TT;N3N zvZ-6p^QoGNsnlWdX&+PGb3&WL)j=kvpnP8+K}^LD4;~-5f6Lb@=*7gZ`Ul5i1K?`* z^=V{v_oiSwwdN){@sZJw7d;m%c7tTWi%L&uk#tSoSmqA@&6dq^LVTC|E`IC}iANw$ zKm|%2;g&<|kBpi<+PJHcBz@RppTv_H=Q+>hIf{JkB5NGlQlfu?1Faq~#ByoVEi?gh z(omQhwhp@l^o5y2FZ(?kVi?pJQ{n7Wx;_n94MVSbyZ2Rena8xo#t@7C)Qpy^ExTkD zb=^C9we}gla$nyNl#==GwA_eso}f4LQwqQBeP*wwhg_%!EWqHHUcyO77VbzjhAGH5 zVr{E;HjpNZPT2AZIV?cm)_3J*pnWYW(-5b;HXwUasT&+%+JLoGK~cIT^Ek(OXThqGkxtw#&VNAlGC<>T%@ZrlJy zT=&EfeN#7zK?W~h^9vD&ZuvdK-*<~sk=QSGE`CFU-2^=`%k8Z#kABnh2Qc_Ygq~d! zX^k9f0+w!5O~&<8dVtHsRG)?xwgzHb z+^4bw9N%M=kLj!-M^@x1Q9pl^oYhm{I)diZ*R*|ZBkrPwu3RC?D;J7Ui`jTsyw z(C~jOMv5z&b;r>i7hBCX3O4P1r{MLvJXLLEXl4enpn|u6Wn-|`VG$U_*>xmfB8+h$ z_+qEV)7!XQ&8L~%pAho@TIwL=Y}#HFaa|=2#AYeHw0i!v$|RcLf%gOIp+M~0mOHae zm9J)mo;A8~JJ|X~Gsvi?i+Sd~oT}Drc>6M6H3PEui^*=b5tB5_w5^ai-9tSnFdkn% z{h?fa$%J2z=FEn7C6CzoUI)!0-!4Aa*B=`j;rz3*%RPzA5#DVlG2h($$>7VU@M zeH5Z+WHv|)Kn>y!^b}RP(Mtzlkt~&ejkGfYlnI+K-6i*yD;`0&;aD;f<$*NCsp^;5 zO8FK8X-bRV!?Pay-wI@k6LI~bU38a}MLobJ<#ohuMg}bMHOgX!>emC;3vxyewCV>Y zTjl%fg-diBvg_} zN5X;%g9Yn&@d@RAL)@IefbGER=WdVXpNd?7Em@e1c*@ax)Tg-CY=O_GGf4PSp^#i=AI1PcENK!TL(aci z6knQo6qS{88Dt`UByi}hUhK4dPI_oD)N*}P5@pfa!_}h5L2+*|VUjT$Jz$Kcj(9Tm4TGsNElhouwSs67&Tvb>lH| z0{v^Kdv0@klb0|){8(Z9)vwKo3@u^vS>1>6tPpgTq!v&%-6CvDJ8x$LtnzcZ?zPoWw%lRA@XimmlEx<-&niD9Ke8ceP*@pAPj?wBEd4>aGdBHr z&)T8=B1hWKo2)_jd38sM7W*%!}y=2S~e<6T6E0Rl_K zcKmhx&YbKdfpPWo(Pu5K2U;n7QpL2N(0v&d656(A5%awN?ofvzt2m~Yohpkj`Sk89 zcXPE|KAH18p;Ag0`}j1HTIuTiC@X?WSiQ;ZC{H~{{;-oF^2glGwevTa5dYFCMPHGs z>NCnyonUtyjWKu|GwDjJfoN=~!Y;$W?? zOPDs()ERa?Hp6)tXHrKI!rQc;e}1teK;q9|SiY5&y!9(-?z}Dhn#KBO@=2!{|Dw{Ssk8DQ6(p-O&v#Mo6XWVz1 z;@EUJH9~mn{1!$E_tCSZeS>qhA2KGE!s9^-TfqD$tZEf#=iI`*OMp>N^T1f*{;nvO z|8q4~50AGcl(9H+X+A48N2WHK&+1!py@b@8?a@*(@!6{&WT9{mC6x~kzM>S_i4cz! z8x)+~J4(N~{Je)im=Kf-_yVkFrfA5K`L59X<5<=M?O4>xE2>fm-Z5-khG5}U8A*yZ(5W-^7dBlh0_{;xn0`YGxZLK zoT%=_f*uP5`17c3W_L$6M|6w={nqiQMLyI!Z9G*<=FM0iDbOf0XJAU3a8-5I%xqh*V@E?gy-n`i~>N;#O;t}JN#69>~zD*hy_fN4o)d` z1)ru2SC+_iOjfQN5x2vK6Gm+W0eQI8O9hUz+oPsSS zFM-oQh@?clDW{VVM9DO$K&Q|Ez9!~0WkJH?1pU9dBOgM2cu+%|^56lTvCNUXP@}5* zewW3!lV`cSA`i2@AGiCp36q(3#5XOB3r939jq61;{jPW3QQnm9&>ODMp>iL4IoB+@ z^wMI#u;!1V3~-9uM`W~O#nE~4u=^7hIErIoi!UFh3Sxb7W6 zy-w#b77*JWEpbVu`#ZQ?=qE0{uaycuhfzL8z@evfrbEsaHSgjEZtop`aDXv zw;17Y$$k8E!Nrs^v6nS?f zh#*OgDuqoHAlZBjVfKYnEE>51t--^Ex(^Sd;&w1RjDAHCd8GfkXvgMilmG=W)2!u6 zTQIR3H_h9BDH|-MXw6aCL`6SRyDkw1>0m|>#;cv))f7lSyf+7mO{{DiVoCWdFUm1n z*PfNI#tOH{TK4sn@6HRbi79c8nU+`#=Ug8)gQn*W{e;x;Jwgy(Rx{pH(;J@ucRwmh z0K$z8H}cpE+%**|kP{QTebQFHRXz2@k8WqYb>l+#?WU|<^BGe(wP-RZ->Dr_#CE?< z4aK^Hikn1MNlrhKSE8#u^!DxT1HGD8k0ipwEHZa`9m<^xS!M=eF)H~RWebMTHhz8e zZ$qsa1kz}=?kT}=e+(i{uNY!_Pg)iy|lju&%VCMvr zTcYmA*}~hk44fup#THpdMB~{1LGc+gA(sq5Rid=sQ04P*7ySrI!PVm_yU!ie?qTD? zpS-Tl4~G=E)NZb?*8K6sJ^{$_0|fWAF(dCB0Ci)(Wf>dwBG+ zZ+)oJN@HG3+-8)$Z&TvC)HSw6*I-rkn-=F|tEbnd4RmVH3=sA8gX6Em{EI4_vkTwH zce(~*kQ*EOaKT^tUEsq=IwrV8wx z`|V-=-}Ii(Z{*O%zsbJgNzYpx7Y<4MAaZ@Ws99yLWj$U>*L1nx!)uH{IUj4eCY`Jc zNPK5IQ631E_{q~L!TzS-dgUW*tdCq*7~$8UE4(|F=B&n@QFN`IUcV5&=K1xnHd-Mh z*lOkU+MQ_J_XEgT9wWNzt2m9C6_FTLs?EfKoe|vuQjr^JhXnKg?@NLLxvMX}hY_ar z77&%bs$XNO|IZ})GZ%M{Yd{?`&bri0b(q6VCCn#G(1PAk_%s3 zx%OAv(=N@H$CeGMbBU(NmL?KJ{0~nC#dn>;C)QMm2*6Ly9r;w zxWkjC9oM-x|Fj zW~5sJ+z8wlW(2)_bz-M~bT<1zR`9k4 z`kq8C5G+(gk2D+mDm+|$4>jefe;BS$8qbG{5_^8W84{IoF%IS*T#K=%X>kwZ{Q;lotnD79U%a_NPT@n^3(<^xu=qvBOAdryNq+r-_(=;RMmdYbp< z=9_Ythz8_83OGEe9hh@n`MAf6Yi1x#3wnk74Kq2#@lin|x%tKIhAzeiBjb>p{rv+B zY8Q#5p!Oc3Gee($Wx0RpCybRLY&yj_?Nh%64aTvJ3dR7v_JKHa;aNMo@x<2K`7Vz% z5m%BFyQ0m1!~;Ja9f|jIm%AFg6RUizG^YS`o);U4)n|_#$)kgoF$NWwRs&pSd;~=Wh5r)-(FPH4 z7oZ&i`~{0hhO+>rNy2=d4aoS{kE_i|mzWlmx}%*3j&1h8rHsRt`ciai2aIa%>8e}p z-zq{OVq7_4Yi!P2r9q^J`omeLp^O2JGk(q`eXmgH&OJe4u@r{TMWW;X^QylGC=~G@ zef)EUwvG3NiUWKYN2!2Y)P6w1GZ!B3)7|IXJ@&(-kYJ&dwXpR15brxvl6y~B_wVP6q6yoa`2obf5h>;?XJaqpj6X7j#`W0bFd_p0&H z&VN;MbE{9K6ZofG`*)m>(1BLlPCRL}R@8i>?n((|FDw2eg<#b!&)zllIxr7nF;O~5 z&+yS%1|iGRM>$WKJi3nCp3Ydl>D1>q6=;$2-|TQgWTC|I_WO$gam z2UpKtHYt{M4}2wh;+p+(vQmvtfSk|vLnEC4@qcdqZK&AguB6%GH_LW5F=|KxoL`mR z_>tYLz6;O5lrp`U3c~sL-h{9S1OK2htMYbuPX!lLMwZxetoWmS`pw}_m7Jch!IK2C zDcsuIlO}|rq7?ikgWpT{Ac{;<^ z1BR;5Fsegue6%^C;ld*h!seFsiEO%}%wwfN62*1c2&0PJz$ zxMpo^AQ(BLGD66>GES%Fu9*%`M)mB1^(?+JTp=|bJ=jCUHYeTfWZ7gltTzYgHzeyj z9tMrq=No(c1dLdOvgs$mhgIe;R65cVVewh*k3lsazFt%_n{WHc5%f*Q!@f@ZzeWx| zU^1xIyfQFbd;)_{@?{aB+AOpMx5l0URAZyZfVQ$-V?-}=Qa?m&4HT<5`tC21#GKYO z=f&o#U+TTk>Ngq9^%j*pPp&{0r8dAM3a2w}A60WbAe=yb`u$+EQ0_8jXx#Fb?f>ML zQYKVy`0Lz#p=x*L``S~RFec> zo7;Z>&l{uo1`wPAt}HGbQ@TCBH^LwI^QAnr5R}~@pbtJ6<5#3@m+K!(mR0%m^R{w> zn1)9ti!aDA*e9T*>anrL=FyQvRC&;q$&g~J^LQ8AEwGu;-qN<+>Q9SK!lgD9lT2+G zpM+TakJdmU6P7nUEn`keE`}i#495pHyft1X>B0lz5{Mn_j+(R_fvKK)@=70EV?K&m zluOI56(qZ#Y?E#u0M_(VbJ+Vwx~*E;Z8^iu+S!9gYTCdj4ZK2q0tK(y;3Qu%sC`B|wks^{ta z`@`QUcJH>yksJ|{s3@`j6)y@f&OJP}pMZV4%WVSnJX=nz1T7RH&+|&x`45HWV(F0) zG~&t>e73Nel{xPl&js8HWEtLg;QljL>5KA5A)U$8xq}_F4svQS4*?|7X=5V>)wLvO z8L9o8-*2T?r#GTA4u15J2z3KgL%=k>FBfTmiTuiwaJwsbQ4^oW{<)9R0#*LJ5YVZ& z11~O-F?&EBk*|nZJ9cwt(oFR`_J(-oezxL)))id>!S|r`rXA<1tk-ph4=#`0ZP^tz#hYto}KK>sQ{hIJzh0jWH+U< zvN8&`H)&I%mc(PW`klfq>}=MdgUUHT%HcTf8UWnRt+#sE`FJ)xC#y3J&OZU&p^OCj zA*5{Tpu~IeYGZdtvOiVu3*Z%j=bX7N+-`nT!a(!PC7(N&2CW z&a+=Ole;&Ch_5z5m4UAWpC~qHcq^^Ps6n%1FQDjc^y^|4aOp}j5oOxjpqt5kg4QrY zT|l03;40E&(vD?~#I9&YgB$wCwNtMeIF{-v-mIwy>B>J-Wse19$vI$Nf|}+4kS%c) zw%DXW(!vg#8pp#X9VubKqJ9t4V)sOEs;7}1v1>YQm+uK(mrX>dp%uS5x}=#8v}xYl z5_yG`QMoy$0?lNv9&tJlhXS@H;6kLKot`|@$6`vT{1H2&=f_+nsvN7pvVaGCjjZT} zj?3Nc=3xsJ7QG*$wWg>bG9fZBFHb#pw%qSwo&hzNbC$3KBR6LqqZ^{>l zOKgo4B+^Z?k6sXR$w_x7qqsA+ch>?h)xAx-iz{vk_8yCIU#yjK*6ZSB`FW$}r@CzX zdB!<*<#Hbqn{*8)F=1Mflj+xO4kfiTjth)=6U&jf?%55#`^52>@XhZWti}%6-UYcn z0}3n*hS6yxpOx?6oVa{^la?tRigj`Zs4^vH?Qdb0FqgNs^m>cs`(qnR&W{=l>5Qd+ z#*EPvs3R8Kebb15e68x8pe?dbrS#!}uY5df{1&ZvgPa6?i^bpp=q}>6KjW6^kZXVkAIU2#TZ74X?_5g_bpx1ngk_v=3l7w7l4a*4It}M@*p><^d&{uW zNuG95vVA(}bk@Dvd`CA(xO-g|ZmZNIXH@fr25)GoKG28MK-gU5S&q-q4@rEpUa^cF zI%7tXmM>W1uYW7vPm51WOS9nbA~;J+M^q&C17g@D|G2xD2XsXlOv87G8I1w&r8Gt_ z!1a6yt{W7~=uB10|8uLA;vbAJ*WGTr^YZZb_3|bwGMkY(IeEIW-3#=>wwFni^t;2V_eX}yp+9U zVofYK*vx@PuFU7S&_byipS>P&P|sRUe4Gd<=w*_k{Pt?OJN~;JukT$61d)%aujB%U z@!rtP^Kb90sxu_b$DG*H_kI&M+OWplLB;FtFUt`S(`27M^5O*G?wTMn3ycy3VS=>Y zgJcUf#^zGjwe&)lc#%d6`~8h7A|A=%cr?P0km|-Ou9z}RB>DyI<(!g5Fb_2Zh3oA& zOBh{{J8xyNxOR?@6X+@%8A(7uArdcfdW$Q$gAFw1nMsNDn^d0iOzJebIZf6&WPR0p zP<}>GN`Fw+UBg;zH+TommlAG0R{T=nxqlPydwiN#XSJ>q2E@SV8NFapdtTPxPrwdr z;O<=*Ci#ri*cr`tT?P>{Ea*s5#l;Dk1srDcyIS5}__QN?&hWtU2Q8o(Ty~AGv{1;T zl@QKv#3%H0qFd75pQyeS1r3+=-wnT^@sNaBzhFs3eeRdNr%@<7_d699`vmraa+Qmo zRb>)0E5pDEgu);KCo@x)H(s?tOnvM z$3Y4zs)G28R+7&V;OVhdej9lnX}v6+UZewi{X^*GnG>o*lC!6VmHXK?mJSX z$3J0WpVkjMji6z2Z?N7UJBmt-*B=8ZcjFF-A00H_BFNZ@i>kn_ifvkBIhgt5)lf8( zqxLx`Xh&wiIVtTY6)_NpLA3X>QQ2BcXP`r z%XNTQDrTcT41hfc>5w?Bw+1vlxvRoof3bSlp`haU@oGwy_DWpO{riM13A3y*NQ?ji z$;C4H)h#?=AlUsV^z;~SK-Ork3r!eWvNQZ49}9V2DC1OY5J#V$N)HrP<6&wZd{Fjw z38hpjY~HJjlbUI%dqYPaN6w9pqm$nDzw)QJ-KQ_bG$x7^5etaK%-AzP$|qx)eD4NFyEw(acot!z ztEVDmmQ+N)wtS>7JU}<#4IJ$Q2^;E7;KQX=b-io1ou8?YE*+5WdmlG0h1MO=+J9VD4* zg8#69aW;MrU?)DOsgO&~-Npgyl)<&K5;o#efz!{crq{qY#~82~`H-hdM3bBv)zdGP zQ;3A4f{&pto-LN?V^f@FlRwLpF|T`8x(1ty51n>^XlmFgk};s{h&w`onFmfN_>MWy zO`f3iaQNXQGuM8rCOJ&(m6_#tI22OBhH(R%= z`)}zL0CB(v7pKK=eq`pCJ3Cmx=P+2RwMNGxfq5o$qU;lt5jm`!8zprE`g@g0D9PeP z@*xu_2eO&VB3?Ol+Et`w+AVoS#l+B0ue@IFmTnXkj1j~<-Ae2nEbu)%+Swcnfr!~@ z+H2yGMAC>;YfYF7)B-%w&9v(l8VZDqIHH;jLO7HHs=9MJ++9m$3@Ylc>Xyms*K$*W z60Pz&bqYjYHEi)cD>Dgvi<|wt9|%_iY(5oM5tODXGmGvb(CVAtYRwW0Id9v3B1`0t zM|W>t`C^)=Ye@M5$=9?kegVW$fC+y2XT7(tnN#|nx*u=S8OA>V72P^W75WP$dyTIE z$lo`1Fjs^EU%Zs!0<~N#v0K_@3QL`iCx@u3(;HV`ps7#7d~deiTE;CEI0Xt|-FKm+ zZ0jo3u@7sZxp5rhpXFKPNL;fXEYLeBYSXLGYyHL?2>7W4-P}5fs%9j3#nw;S##F=(O-Y*0i8>{{pRd3lH zEnG`JyIBu|>)=b!2Bu%zsIjGX-V0FGY8hasIZn%0%X+%Xat?F|L1^tZ@$k!Y0M3w2 z>tP@?`4JP-Ydf_5@OtY8=!Git)0oblAcmj?^E_G{#$^~d?%AD&Hh{Foa-9n!f5^1t z`T7?EwYz`t>#}2D06tkwM>vHHC}FbSQ@hDd-Xfn8%xlo4QLb3gP*;y}OuMA`iceQN z(8jjsA0_5_9w6djHYqqHNJ-2gC@5&C^;qzR@slRP#7X}RPm9xrwg#`?3E~wJc0YPv zRa2te{E%7k>(~5vP*9}Wr>}hOm;KF5Age%UaZ{}}M+_vXAy{}KrJ1cxGhbP6?TX|* zA9!(0GN@s4a>1PD@?^}LHKBAj2JM~$#A#zxON<;7fAfsxDgcvs6+rHVTP$KRP1U0% zMk=5?&X7j%{^if}5m|aNY^Ylcf2oCg;mhxSOzNXh&C6?pEnltA${+J4!xFI$a$x`g zeE#`)M67MD$(SuY8@4&%Vs4c-H`AM@l4baB29 z6&9+W{ZMZ;Ha@o8!QKofSMtNq2H-^WxTNJ;ozG`JSXliKs)<7nO1#&gEY2tw^FE?& zezP`27a6>{5ErDE|68{DeT~P?j|?=crs1eXXDTrdc=&v$bXN=`3?3~U6M_MIj=GU( z`7V#F2c{w-86T{fPZAk3jGn&V+DepjXqS zGP^;&rq$KBs|8>hpL@83qoL+Nv}2i+0+dKD9153JQe)@>u`@A!9(H_NX#mz+H4r)S<8_L-PI5R?S>- z9O`RRy9^-PGu(mUn5Bn&5C$-fzfK-?Bk;Ci=W#t!3grnvUUc}-t{&O$neY1$cO!Gx z-EX!J8L7uiwN;V^uCGs8HXMIq<0%i6jhL>-O$bi63=IwS4&%NtxSazbE7;3afVno6 z55|J?Dqn*0SH>PaEfLzh;X=iG#xl8bRINdNSB=A0!i{zBl^;(gU;5%q4Fjt`&~G8< z*zjg8)Aol+_Oj%dgzPQ_48CWBa_rJ+i{Q8*?}!uzsPB^XQ{KJVBI_TY9(ON%Dh)dM zsUpr&jyDki)R|bZKen~veX4B6ii1uPkfMxAMOFMXeoBqd#EY)klX79Zt;wp%M{vs5 zvz1n{JWtto0iVekF)E`&g5jpaVjDp%DkOwvU&#yhZ4~tU05K^N^uTTZqX7LQ-74oo zx#pkVqJRm;}7WBH4WmhOuCpypRyY^!cSyf zzM*K%)?GFrg!$>MR3yImw4;hhH>sUs)(#Tm;|M0ihjmEb_qpg|cq(J;dhuB?3FKHo zGK`or7!+9o@0SmI`s`4F6ECuleK8V9_!h=7qHpOBdUU94M=c>0>5F7rxmuFvY#ni!|jnv>=z2rG{l#oN-AuT?jpp6y`>@&&3 zq=c%09XLMy#m*><`dFg3nZbc3eE7X|%Zov#Xki?4BaJ!+7xyDj*kV3(1KnjDak+Q~ z(v}Kvv~u7X8)|6Bx6OIe0A5fx&TkkViV52Pl3>sjRiH@Wdzl~5#Skg-gfo$?$MNtK zU6?$m`gQi=c#49Zu*OETb0HrKX=wtN5vNE}3E;~Lt2Y)rj$7qjTOtpk32621_&bBr z2F6l{P?tmoHPix=_K?BbJ`dk0&=Tl}B!JGwVO6@&E1yc2c2(dNbB|8l!g+UtjvGLC zSjG<84}D+Qp;s`e0cE`k=8z38yE zYiuPl^uBUpJn7gS_YL$a87uhFdTQphHpI9A%^f;Q}$rg?wrjrphX;U(!tZ)2wx-H7ok;#J%AE~wZ6vG61ii@YqP zRFD|r`>S7%h2}7}`S+RER|uRPle;uCtHZw=@M&!1sk^j+P2O-?i5T#R1ceAaUJJ9^ z#)2Z_{o6-f%;{mda7pT(F-OpPu4oju|8)*X9g6Z-PaoceBph?(2KtPA(O}-f)zODU zVxP`CzCA%$Hd|QWF)(SRcmh>af6xg2MDJc*meodK#Pag7>?9GeB`R?*!1Skc8Hv5L zAzPW2B%b^v6R0Rs_(QOdH45~21oXleOc)VUuS4C8_adZazcV9j>(+c5(Du0V<9{V73RgBJ zgo>TxQYYQ?j_Vf)sN%Dgy=2bss!)%H3#>MTKv5M^=`mK9=5pb;%yiOwT8v~OsY&@! zWDI4%;GJaSym$9|xH_g+>;NdN8%8a|>$ z0-KB4gOCvTZBG3kpX7u4cB&L9@tgO{RGXV%%&4Qm!+};!<*{&!r z0WBpB^T=qPg>GsyG*vvr(U=| z`nH4a9@XAT>+uRu1YusftCL>K5V8(*!jHW>GYvXp_9on%FVF9+1s8*-QtaEFu_P=w zyrRS;;nauM4(yDVF+?O}+>eJ2HQ!c;-cFI9x9L#n~VsPg#wj3i2FK4&q!I=JU(kcz%jd++a>4K*%Hq}_LG6& z;4X{1QsdF6p8PF3atuzO59_Zt_FzCtJ|D1tLDTh^e;-VKVOP16$Z5c;C8i8P)dgt8 zv@1CMvpix!FkN>D^t{Oqu6R7WwSe$uFmslEwJqrLxuI$q-o z!|&H@2VOuU5FPT>nf4PnvB|zHThgN%?}L=%ZS3>yW8?==dX>J6TY4iDf2qCH9pA%i zSO6q`YOQUSX)aIKDr`T4k%D1^w!s&T&v%34D~rIR7ZTG@rjSQQN5+QC7Nxc@fi6+E zc0eUnK+WA>)sF{KY-cWS)DU5a+nHWC)2~HPTKw`i5b_g+ zQRfZB`$JQT?%x-JpxweD*gm*i3^K(5PoKHr@az++A7gB}ipeDn^pUQ#7@)o@>Q-q^ zt)qiK-JQT;ll(a}`K{vDUOHn;zZj9Qz|RF2C7U-M;dH(dA^?aDrBwsw$a#A@H+1h{ zqof5n*sEjEg_z{e_9GI|&fDDiHnJiLvJ6(?pbQx?lcH+tJr))@8d9DN(r$EivK*;I zjk0mGoTX=c>9$89@+u()bA8oBoZp6>Hpk0aJ!dU97I1&ULU5gm;P*T~3e^uY-7Fu3 zaMX(lvthz-a~aJV7)yH%OKovRAWvV0UH;Nlp)7-ZQM`-X%LD6Hr zU~p4@_d)x{f$PC9)Pi~qNrbx6;p8R2s`*RxXXiEn4{8(GXRVZgos0$qYr^25z8jhS z0gY1y;#tO%{sHkD5!6u`qa~oo&dhEG6h?Tn{l~PgFT_X}6{vN9E>NP*r`>l#8l62t z$E1D9@I+Fzjg^zpxNNoEcRu=1@)r>71IY{Uk{^-trAtB(Akof2H+S#2yCm)mp5sz= zE|KGtv+#bh&=uaN0WY{t<~D-C$%FGeOIU*%;-84-y+mDNLc$Vu!My`8K=YYsXMo@3 z+d}8Ry$|J=g+zi)&5Aj%wjN>ity0zFwIOHyUdcag@Bi#u)D$UL*FrES&{yrNyjmyC z^t-9`)|Eq^Ws@!h=c3USTAx27{>4a^nfVe_aaDX6XA0o_fBie#BL!K5*yFb&o#6Qa zrd^JoL20hogOf1e}oB*!;(`j8?Y$xOyi`UoI< zE!q8lsQSvVD73a~LX;3t5D=skP>@nOL{LP8Ap`{mY3Xi>Aw@v}=|(_Ok?!sWY3VNM zF6r-{^Ss~tKIg~rI)8Nb?AiN{b+5Iy-Z_wF{RcH)(QJ;CN0<>Jq-3K;NLj(tBW+ga zcRMvSoj|IAfz`e+p;mhvB6#qa&m*99k8>0!O)Rx|IRx+|e9a^5*TCqOfR$nzAx z`+pOo*S7b^%MzXX`b;EuS}vw1t-a8yyGyHW@JHcqUL#A@Xcx&t!^bf4n{# z-Cb0r87sBcwW*kR!=ISC>*JB?x~r0*EZg?X?9|cG#dPWo^KG;`I_kr_902?cR;>iW z#`zQ6&yO+*dVBeS=Kfj{PEK^7RGH}Q^n0*4pm*OcvG2tbQD6N8&LlxuQiADtJgB#+ zU{2yWZG)N8c)b)L zm=^_~!nuk?ajb$FGZzSMFw>7O5(9Dkfw02X1+L@aI-^?uj?IvIzs>wa$((yM)3m6g zw*F&et3^=wz~Lk9g<*;p+vsKr)dg_=E3St#rp?UT>V@BZNea<|9@Bc0cei;~NuH*4 z-?qr7V*TWX#sSxFDL%oY=6F&l7YwWf`?ILMv{(zpo~NyMxUphzk*9MEE1pGAhx}U0 zpEm?Mil8b9rZPNUKjh{io-a}pk&m!T1u&9*e39RLG|b4BUF$JQ4o%js7<^Bw1`P~)%Kd}KmzEI`tp`v7et=YQRAQw%e&=8&BgTXvqzIf zXcE>S47s^@fBmF84p?JAW!zi1JIMC-i{nQu+D41d4i`?Ko`Xq!2oqw~oVLBf7o#OJ zzvL&I!xtQWjjgpF9}GO=%wZfhHz7U%Y1eoh2llV$xC6PwXAdg!pL+CNq+!6^CwZ3n zw=gyw;R)wl&DKU)spZ~}X`oeA4^}2pOuZ5QMKJgU^ zc`;H*l@r_x3+braB^DWLrbIV(k}m;kgmnH^pWwUXTGhGFX5^F|!jvEweOAXns=;#fW%|mIfz+!s z8HM@}C)Y#xF7aH?Ew@{~^C&vufzEqAUT(+ie|&1H%g3LAgI0;K0=-D*^bLPqw;au? z4=1NknEJnZNUFLbd`2-cCWYUyj|c)XfJU#!73IBusO9N^?bcCHvwDt5jZ$ z@;epVHo=r`x}Y9*1!#3Ri~Dh>QMw`&;Zx)2du1`4tMltnCK(qACn2CL`!rTD6Pefg zl5lSwI;*e;PSE`mzBbj3{`uL17d`N6I|Vf6iv(N1;4#>UNq&#%yAx61u7;nI+SvPc zl^o5X#yxa;HzeT;vh=c0y-lR}#vAGT&o}lvW=(U&jJIk@D01;BIH=#;cif(pz|0q5 zq(xVTUOVpXM&qSIU@ql(<2(MVAjwNuq4(s;H+JKM?0thss}*Hy-DlW<%YDy*aDY`BWb)~JQc`Yk zTiCS2nHAnU9{olOm^{VA@ND&xKtRZ5&sNZ33}|5DU!`8T7fFKgbGq9kAyAO^LBF5R zjW^{b_P-qw>~`|J$9(Vj1()z3Gg|PGddsr%;h$qJFA$Wmav{B3I)q;s45szE6&rL? zdHo)CG%&fN4ILE1k!uPl-E#_l1x@FO#n#@efbmOTB{GL%f~Ji93I+!6TTE6uK`AEV zNwh!Ne%?XR3ViRqqPaKKFH4j@c6%LJ z?QKk`wRx!9VO+qh{$s@I&VH$G3(>3;2?_2ea1CI3%2KQg0LULhCQLsm5KXDJZ8iW` zmBEX*wgj5!MqTONsq)MKU4@|Cr+wZYcd^LjGa*xfI5k_gQzjx~>Rm8(&hNvS&MQ0F9?7zQLy(LyPZW3g6{E2;Na?rovn3ZRi7y;8XlbsKcK zxa{J4Cucve(~DxGy&7B@o>!NG+L=&0Pm z{|qM+wNWNnF&h2AFu-uZCHdoC2^5QAgHQ%v$z-C`(^oAC~s_~M6U+unL(}~>30LPT=^Z%PdL2`b((6Y zL7_MYC6ms6XaffLhTnl?#B`Zf`U~2NZdBLXgDg64;8Nz^p4}|{TJT0o(v&$6@wwD# z+*jXKECV}8xn2iw38C!tZQ1^3wbD{jK$1se0L|ZTE>fW#5Jqag>|L7J?kkuVMsam1 z$5sdgwg3gwcD~iqk)F!lN6gv8Xby{#xG89HfO>B2=37QgzgQIL$)zvjJo@AeDumBD zf{b$@;}0(_z9sxFGv8FfPBHmdAl^fC|M#oJQin={+4gYOW#Gqr_VNu0N1KXX)sLWr zW}8HIi2x3Y`tn`b80I4l?RU>*(Rvn=%a5K*^hKc0b?jGR2a6fJGayM7Wu@ZT5~Izd zz6C%GtZLw%7q||D{ufX=CZ|E6Vp!}5W;}K1UL8NxF zm?xI>=Kr;UNdlB$=Tne%=3FgQS)lYu;4qXB#EJ(8%GGkjMHn+HdEILqs)s zU)p)Um7V`x?T6ZviF^@l%L9v(&Z}9C2!~*Quv=4}9b1Qq9r2lghO0*O_63Y16@ier z42e7m*l>)rkJ$bfUHRZaMwv_oO&QApkT{gMo|{6~p;s$(`VLsB(kopVW^(b3_wQxJ zBDpdBybgTj{_tCj|F9R4jdeGgeO&o{uW@3s9RDcbDR2^=I66G(ecW9^ekc9*wJm4n zqqvQ}A+))T$<@!c9BAdij&293a+a?4$KFd=m@r9s&s71!?ps|{IhWB;Cmmrih%QpM zm;LEI7P=Ee3-_#G7}0ph$mohb46Z0JU-~udi$zYVupHeX+)2fBwm+oRxcyn_)Q!>! zFI8dGZ~eDSt@HWOB8(-Tugp7}jiX6k$(oVzwPWB=wpk~-hvXF>d7Yyv0t^{o5s44T z<}`aBpQEZ$Pu#%A$2+d}W-qqi$#v1(mk(dyF6zvCBsy1R&+1TI%5;25&Z-MZ<#oB8 zaq5u%-{apWtSG~(vxBiu2~2}Zvo<&2_kW14iiUq;4Fdfn49B)+w@a9C=5u4bq0-OS z3Ogq)lPZ;CoFNC7ct~^KlvuDskpZK>172PyJkROTi7?{Hu=v(7v)gXIWziEc|k`wGEk#$Vn)7uXmW7Z3cAm(tkU}@=)o}UIA)!mDt z2|BM!qRYTL1Nit~hnya5=x)R9&!t_9F#_r1>e-{6RtCQC zdbJF~un6gmQ1M2Gg^@pQLcc?2!?m0VI_pQ#Tu|@PifT?;$7xK0lC>%&94yQ>8rzbx zceF0<^N${hVKFhW5b`^0s2|l_oQNXa_{>M=e>6eya0i4L-a3w<1E3Xz zoLm5bNwLHAJ=+fkWHUB*oz)W^SXv8{g)mj+XS&8~s?olD4r5-hCD;bu+ZTxlp(Rn^doG9-Ui53N@=Dn3zqtR|izQu15 z2J$Swc-A*ZqAxgwwMv8J==j$1qcRHFYmetc<*&L&S)$vDr#s((TQd~Tcjh@Yl$Lgb zBgWKijuy25&EeIOBTy7N%G}#t`BQb8vRwStUQz{be(|m*E=AUD8Z5S|dDV&tHG8h! z6e&`Q_1nHA2oDU_h=2;Z-IKE&v`({`I33L&xrkTV)Zx!r1Xd`Ch(X>ktC)v=PEK|l ze2Iscg9(qpg(XCP(GW#DwvV3$*YLx6;ILD4F>MYE0yu{>QCa3%m5)xh8uD(D29Dm* zR`W=m==!4h)DdKiV~T>x$KS=y)k0<5w+@RQOX7>hk^Vbb8)u~ zR1$iBw`Tgv?M-_xEKzPqsqa%o6Dte6YzKJPv_}kYX>wyKBk%06V-8wG0)b#)aIP#s z2W1j$vg2G)8s)z4A=TJRUlCRs zXYG$|!uBhABY1tH=i7ljf8Wg5+pfxCVzD`FP-IA_E_zVqud?-*=KxcX>DZlzR2UB40x#9BPPZnP_tPkT6n^R^4xrK;ty~Ml6 zkAy3(r5$qcXjZ&G{2on&<)RYLj$^wtat)ytdI1mxmBpb@8ZN>N)7*CS;Mb|%fhl`x zbj4ds`j2$7k+jHig1uV>&}8=HlQLHQ_Gy}cUuR=d^j(;oD6X;m0>FVPwC<gDryf1jU2M28(9ic#POdGcz&Ay;>UOoi^ z7-EYVl8tFrh z!L2G2d-GB}47-ZCauc~k!_|{}qzZp;6XUpdZDE`c4 zB(!7BGqX-f1y^I%XF?8F`e{kIl_wA!WsO#`?ts|(XZ_agzOVk*?SZyP%j>Z{ts!;k z5~pN-Lm`c)H!Ws?a{NB^-gC49HYY|c5^iIenNNzg^r+vo`d z>xR#2>rBnxHU{}h(To$Sxw2xqYuN@S6w`Nq@xw==# zh9bDZe%5NYDdryNm#Wv1R;3cv?4GJd%r>Fq{Ps1-ygKB3y9DO0_F7(59lr27q|jyL=*Hu-y;lfPyT^My;%!D< zW(CxkSeZ5+j>9CL(3(F7-!a&4s{jluEw{F=$&eUkD$({t~t$XIKv6-3LkEM6brcn-A-YpUl|DIFOrO!p+UJ_AesG z%0vo56_DrJ{urcEc7p4L{cOUP1uLiBYtDO>3Wn_i9luq_S+q^jlTPLaxsTR52wb>8k8>EL@SDmccX~6 zHik>z7lIf-WzvLsJRJJlqnppLF$=IO79SmJ;f3A!p7-)d2?g5(#{k$w5jFKZ?$5Vl zEZ^6qw@td&)hdl;szllm<=Q2f5KFMiD?dJ(C(Yp_QyGlK=m@W9;{F)(P}iHfKsja~Xh*ALr2yOlE_cDsMmEoVAV5LB9f|Yj0^2_7o6mljX81y3YWqgnf7QtfH3Zudj8PhQJM&B%dDNRjZ zcXg)ffc?&*qG_g_QPZ9dMEMn@s;X*|ZU@pnNf8Ej!F`zm{N3ITS9Tfl@78s0e6k@C*T~ic63c;3K6qa(8eu^Hx!GW|80=r zb&@4P->1hHr?&9ZgF#56@=Jq$q4=O0HC@}fr&~7lYbPf!zWvDk^`>h3JJC&3yYZ5O zE}@QF%-2m+^JjdtEo+|t8AV$4VbYXzuA&!O?tV`m>TA3p&DoAwB6MK z(1aU#wBylKSjCr-qGx-du#upRa54GhloG~NV{r`vP$$k+s<(veOrGyu0*%(Q|3K4Z z0Go;Tfg&p;bxG>mU1Dot=-(Vu3l5{6 zYpWZkOhE~&+0rBGR5Xn}M#Pygrgz*30Z@?Nw+2OOYyMMKmgV@ZXD8}`y>dcV!L^Mt z3UW52_VVZJq4(C(V=JfdazhNeBM!-t;4rat@ot3pvBCNILZsG#rl5NhJET^A)p1Vx zT3^}ckYyzUZH0!9bbM$Gj!)OXpe@3nGo}rQ_$+fRfssIoL~wC&rD#?;+l*Vce0F=z z<$N_8uuLQJMxcZv!4!uB>Nzr+#h^LswX>-9z)>n~7Jt6`$#hF+ZtnNh()33VC1)Jb zRd1pQsNdgK9{k2Z(%LD9h&2iqf~p+~?Deq{Nj2Hg39bBn{_R6EgKYMq?Assx7E|M- z?vU*~CC$ysp^?WUyCX&wThBji%I0RujAfB?}_rza%?h4({Ih8(zs zS#K2h@7KMuQko`2kQrY?PTovwl9rP4?A^vVi!<*K7&NhJxgDjlspdZe z*P#1=Z3e;E__OAIQg`pf>3D2ZxYMJvC?n^ri_R**@?j9pLixM+_B4+N{D$Y;Lsz0o{p}l-@c|BE4Q$T-j z8`L5R2-mNsKd+a-LW4gFhcZ4e?qfs*a5@VTBHluY_xXs@i5L1xCalaVS6@FreJH}g zcZ}>+i&Ryk6*xUAzMh|usy-lJ$a#iiMx1MB^)8w2kA05=xdT2_Vz;Bu-oz5n^E9qZ z$w{A8jH?RzAqKuZ!G^99xUWTys~c(>%U#+SqbD}~=HIs(N+wiTD8gD1m=QLn{I|`QdwD>x1+~U%EF;8ffqXPk^;dNTV3lFl`0E|`n1ekJo zdt>=7-u%%$0qFDsjM~`_x$1^do|4x;acHAFQm+<2SGT)#t9($&&XJnZ*cmr-l|yG`tb_AG{AJZ0hTyk&2jiSY1RS<<4LvN z)L4hz%b88qy7wWares2~!M(J|1mtWx4`Av`l+nEFf;YV zX9_f9&A+0#p(Fuo%kexLut*lu@zf(e3S2G@;$OaA;TNU6dI%dZ7bq$X>Q31>Jqi4L zIdGJLmJX3|Fmup;OPTDTQ700eQ^F@n~;|2;0k`FW<9yT&T;HQX8 zzX!M0VAsTh7x1cAEn;!nKDIxG&3z@a$ROJFb%^*MJTIGmPmq~KV`}@1{-rvjATjX+ z8uh;wUpbprRfH9lEBI%3kLFXYbr>5#O6vR}%+@#xi*AS}bHup-; zI<$X&H|=|yN~c_;C8y{;o&w!L_nhFh6N`|q4;>Jb^O7mh?JE}ARfnBcD)TVfosi&j z`RwyeT((KLwQ+F2JljPR>M+A4?Y3=~T(-@!y{}fqpGma^SF^M-(bKfO_+DZ|CMKpX z^4bNp{+HdpFI`3pjX84CPy8f3^e-OYfqHCh0Hb5=X0rboMTN-rkNtmcS5#~-2BPNd zt(2^%m{i{UqXVseOz#?7ahCJjMVHU}vx^anl?;}ID-1a$)h{0#Ij5ceo|WmptZ=2# zQbU~UM3YG{@SOjK8Ci}R%gTC$L`TS(wrKx;7me_1JAB)Szd$h8K00$te^c~R|8|61 z=@bxLT!cLhW69}jxnwC02@+g(+FYA?mVp5J>g(q1^iaVt3Y%L_E?^Lj$_&@)1Ozf# z!Ecin>hr3~o7%uFZQpxn1=7pBCvc|x&}RztN@fDJLbIP3*s^IeY!&IReZ9?g_!%X0 zg|eDxQEGc7Cyvc#d~Ecz-#J>u?pvj+62npPk!a`8!2Hz!cOP|1d+|sM@?TD}(#7ZG z4kDS(Pw4-$yLGjW$q3Isa{F#nq4Qa**011Ngc6mgTjero_6xe_++1Ak7Vm4!0cFg` zdlxvck{KtY@FJx+`*4|Qt*opJRaswA6H{fEmbnfpvMN6#g-*O&w)g&dR9Kle1?qg@ zt9+pm=3BY&&2K5och6BTAvR|_nv-t2srkLWY6Y038g z7-+JUg86$Y@s0nEgmb`UEh>+^DAFxwVm77zj&IPg=^v+LtVuR#$CgyM?0hrXyVWM*B?%g7;h z+b_$y(@+@bdhNiVpJ13)+t`U%c|D+xFV}P*++H*}ETVy~D-~7@58oKXioR2|uOm zM$q#MKb0k|U_BhUB734Y<)ABlRVE=S=nSf{QSNd*`Hfz!j< zzUWIG!p#z>uew@m2aU;t1Og=5#uWC+n1e9a%gkXim5C9X%#e>I5^^y&k)AQ!rDj33 zb(!z%(+qW3rvDymH+o4Em`wZQjV6rMZ(c-Pu0%=zgxuAY9vO^}!Es=$EeB>i z;T5Fj+2sekv4MGti#39EcGEKiU9~EI+bWv*^k!875}fxzowOAz8^&@b_tLYpb?-V@ zFdkXTO8+=~HHpxb5!Rm=(ZD)#x_dyv`I%A7qo~B!Z7bjq+c0zSaBakPlW;3C6_R^| zO*_n!7hsr?J1_DQ#(n@AeksDSn;EscT1CK7T1&!RwHMVy1qGBTwQkl&G?vYO#TtxL z&PavGSIkP-a0QsAUwGJqAvYyaE*KE2eqZT&M}4C;L9oegD{)SU%;j%!YU+TdSZo3c z$)^>49Era%($Lnr2_3+T-o68#px z>kQ9RSVQOb@6_fe{4*J#KxxxhH(tl1A-2C${5=7PTx<|)f;|{82fsb>x_SkPHn@>c z(&e=vWsc$=u)|2Ilc_uDXG>SCnFnV znkh-=`rFFGzJt9)K*6#&n9=jSQ@;f2bu^(k{-jfzT^p3D;aLvqrp{vq9czDvj)Zxc zOB~ed)>LF-^(V)lOBcAL9a3`obHM4y`Eo!Ga|v&zdpa|uC7|bDueiM-g@hJLr@ltT z3I;N2qkkIAMO`wcGPS^v1N7?=HSiE=&HViQr!MlF*|B6LKpK@-B2tNfz;ZtE6dK84 zKapHO9L<)k6;O7|c?STL2gBc!QYP2ZYq79@L=}kMedvBH1!~dWzmFCc%UGCS{@pg) zF5+`vjt`i-v8lLKcXebkyLg%Y?&LKS*9_ZvKYs$fdgtUoLh(A$U>4+UrGu#{GE9mmUaQ25|sky8A+N;e`cX&G^>0Sx_Js#VdEFP_Xt0 zLYF!ZczST0@T=X@>=ti;x(52@yucRDdEdm;A+2TTxre?xHnh{`YfTuYp!kWPy~S|G zDY%>xngUzfmneQ^c_=3V?IyzoX*$-{w zobPgEGXi?U`GtqdmTL|^!nF2`wP1@KF@pi4Wb%!m#2dxd9SY&wpEc zw|}p4=i1!Vbryc!UUKAHpl^J#yReHoZspvRkA#*cF`v>&@mmT~+D(&{igCfeOzl)8 zW>h)ym*z#ptUc=mhq8{<_(O*Mz`d`(CL?J5X(#ncQ9E$y$%2Pi4!h1Yq?z&>;VSf?QX;;2tLEz*oYGF$U*NDjb* z_zv$YG+KFpgqWt~ez$iB>&rqrx2GchJ8Ax(RXt~wwA#8hWMzCF1iz2oa-Gt0D|T^l zRa50({qn(J-%r((nb;(UJ#OUTYQ(|dP}W45V(Ik6^shi$8LgFQr?XVWa)y0Q-p}=$ zF%`?6WUeYpb_e12mGam0WKgOuplt2sZ)LECB-A8pu!I9pM^WZPQ>_1loGh%8p50+*^EK+k%T4Y{0TDpahZ-g^Nj`HGgwrdY#Nt$^d>uwhz$tw4J0@;F1 zBtV~@^~8|)w{=G57H@d~%+rN<RwCCGj!jWL~>})%S+F>$|q- zorBt2dPBuo*%J>vXYa6_I!5RnSehD7FgQJ{=%!m+Pn&bj696QcY)tiHbIHeqRo>g~ z*N4|gG(3k`HBU{X7)&kbV?y=Pr-P_d%J|CYNgfPqljdxyj{d|#UVo!zkkY5A-2|s)H(j$MpDJ!&Xyq3;8n4GZ?&_E@Eue<<%#BlvSSE+;rBl(mvi#@1O4bmZJU? zc`io{MzyJQgM~g;78Vvqx8!!XadXcfMav9I72U#OGk-6%8ycD@$7XqEgDzMS^M2Un zPQ#b|T>OegMQ^75<3B#-9Q_(l5E$j0!Gz=EXuh%v!HtWptiQs%59rDnuX(IfF1a}N8gmtIx-)Y3m)8x) zc@OoJZiPB{c~xtfP4;VJ{Mj65<|#<(x8FNHSn^bIBVgv>i1>%6KwKv7eU-(%LCWni zrt<8)Z>Y=U+vgh*7>L*z!1?Dx5FB}XBG+;xg+#Ord;6k)D%#I97~2FAUP7jCcWv8v zq-ynnP$Tk!@*?tY-qUUzyJCrE>B4i@O{9lXfq`rT<{*WZi;kEZL)J${ax<)E(DdY8}?TY1idIf&+~}x+3nhT13>I04k`V1vG!`Y3@r3}zSA|@ zyCm$A;Inj`!CP7m+mZYDHpvV|wrLtK_f%9HbCNc5ug)eZf(UuT2$L zOrkktRgCMKL5;r@GSJ>fng@;0tr4@ncyW*LapH6fg4}vS1EUGaU{CI7qcgodc+UBs^9_k1oD0IN9*?c&2)+Z%^>)TAEZc9I~hC zVk(2vEQGjpfr5_e!tu~gVvx@p-OWi&u_JMx3@6?FF5T?JD?Vq=s5trB(cB0gr0`bL zjZu*FRdI{PACMd4>cj5%n?0ZzPIVu7R5#eERS3Ppq4-ZVY&O@HrPOWE1BoxW{AxcG z7s|;1b!ooSuEE?DY6{U%4A!!i%UEm_HG&7sRKUNusa9+@Lt^KLmAQ5C;_N9eC#PlL zU~`VaC2E4)rark6e@gqWB8#TCtF7uAA6LT+MOI+*=wPu)Pve-~H$F zrpYe$pOeuzR+!Z#dfrv_04wr!3>pC;7c8Bob$GpmlZB1VKj#{eNGz;?PH}=-`(y04 zgXW)_RpqIhc&w8u7HX0}qwiax_WaKhGqEW?lxOb>>-b4S6@$~3(avZdZ+0!@ZEF);=eemeIjl4crfA$~Pdo!R zGTj%AWkV51=|D(yNvm)#dCOvkuMO^!ultpyi$q7c2koeh+dkBHfUBZEr;u&1qb@fs6W2klF( z6|jI-kk^=5U#?8CM4ydC(4m(W|HJ+(wcAo+K8P6eGxkrXJS(Z&jyHLGHM~S}fE%># zbgu-n2fJgOwzqC5YeNDpL%02yx2$vtIT)|qJf^A0TZ_-%0?wldcm)XzvhGLJsLU4G z%^txlIB2>5J8Cx8>VM)=y(Fc>6p8kFg*%p~hYJ+4Ng~~G0vss)Tkwol1XO}+1YIHz zao`qJAN{!nF;vwsj;x^P(UOMeT2@U*2%_8fpY8OHQ&QT)*oOC#P!!o;W3J#6Fk1F8 zBw-h>rbf&p{E8iWvF*^&?!-%cPaN*lp8>e8#>;q72jk2vEDyEl{j{Y12~X*?aG#O` zZhhC)ivy4?qFQTwr!EDQ1Uuk<LcKm)_dwo(KXl|q;7HA@!@sWEi z;YA=QG1;9zV)$93HyK)=0qD4(BEOY`*}WGg2F?8Zmh?$SC4u~rupXWdd$7foi`AR( z7U26OrcAa40)WKDJ(YSX2vYR=jqs>v#nGV|U9VeZ}s_Cz(fBs;AQbxmtj; zkF)VkM~k{wt3#J%10JW}_k3=O)KCK;pQ&1KS6xJY;(ueRB|pSeq0+z5RG=9ty!Mr% z8>Ms4YKCoB z`1vm;^Sb?}o({W0^EDWQ*}Q1N=UO}s)%8Xm#%b{Gje}3quYLPu58Ou9ZzA~njCHmZcRKD>S6PIGg(u$*@0`L8tG+3*qABA&a1(|?!F(jg0+}A)0dpXf z4M+((jK9Z4?1j29(9xqoYfAL{G9Px2OUA+C?a#1w?}aRPO9OEI%4W=|kGCw+PEpl= zfGKAqp|Qr?D9b+)wskTOHc%zWM#b5$P$8?4)>JDu1&bqGy95`%G60J(I4S9lT8D8< zd(kft-jJ_br$vT|0$`Bjl?hKXPJ~IQ#_pUC?p{8-Xn!kHn}>n$OCIVp`KvuIY1pLm zgr4nNzw4@}b$9WrvU)eJ%HzEJqJ^3R{CqbP>$JT`N1u9ZPxeBwPl;x8Jq2gSNtxkjTWyA(FfmRF6-H6WgyR&Xo>CTOvt4Dt8&q@8pQ?_Uea~;ht7LO@VZt2nzgzv zlYR~T1tUEUq|w$);wq&#@l@L%e+R#`*;KiEz&O}}h&9G_H!9#`6YOO#20F7Uh=0o0 z8H7x4l6mpIR}ts_wSC@q9XBZvsQ1b)1Fe-G7$GXMHli2sKB;&a9P{UJRHpLK?R zb~EUPC;aPe(0rroP%bcw=}EmS|M)0nN&g(}VvcT{ZMsDcTY;yGo`fcz0H>B!h_BT& zr|K?@;&qIQ?-Hh0y0K_0zN5b-f#F$5((_q}1Kzdk7x9G$d}cq7s4=YD(QwTck6Ccj zoQ4x(HMnzXRX^(cQ<~!sn>|RTqOK$G0hDXcqmy>NtKgthe(A&quO4u95?oft*_6S< zQ4uOgsOi9gd34Gq3I0C||IZ&D-*Jj9CQOb)o)D@v`%}&t*H|rp@Rw$V=Q`0hlB%k? z0AV0j@_C~_2wo;M75EhOK%I7?y{=N9;e*p902B z76PAoHs;xImkK9lG?kuP1U}k%F_Nb%sW^lGM~2M(yLX~>4o@Zi*(L1E*?s@jh~G2~ z&Da0;L#}P*ioxlW)#Cdx$Fpqq5r|zz@@!{cWBx-%_ylDKYT>jf5PeYPq>LFq^ehH# zY!u`?a_~8*{)x;>n&C@c{{#4s)X!IwtKGgex@T1ltiZ>qeN)1fuCF`yKlkXL4V;$s zgvz?tt~Dv408Kfutf%~850X&Xu?&N$(r+QG`R8x4y5+3%1<3}XdcMgskrhx&!O+tETEqAS^${o57Hs(5e^Pw!SMG*al3 z!9PFzQyod@%}IB4(iSFt-l!oMkmTM^x3(miA( z&+(s6pn7nVknv_C+d%;0P!guf=A~IivF)v`FQ}yI#`)i47e`X=ngnjGbUzvQ63HEO zi7n*H*ZHplaBVAIjQ(1X`}uA{S_9eo>)K(Jg_2lEwNZRTKFB>g)~S(Rj8$L`J)oBU z3Q2GwGTtyHs}|nYf=^oVaB!}D(X6<2UKk~kQ}0#k>FZN$Qi2^1~#eM3a zs50}($}-7?*_?NGB8^w7%a)S-W~Y9%XwDdU>;qJDlZc5(Xo)^>9slPoa&(ZpT0*++ znp$AYsEhYeWnr)KSh#k@#`8v`EuId&K!)Tp-B^m<5D{Kj#NA|RGt&P)*L^RhA!Z_& z|8XU$snPrPYnn5CT=5uDz(lzqbQ-cW0;1BkF!5D-h23Z(1;n zMv!x;-`u=Q;^tQOHNCFm@+}FPLJ?~9kCtd|pO}Kq@AW_3E(%;ZrX1(fW*?*La#=Gh zOe??_x>^-r;vk9e-3Dx(` zGgc)|Sv2eHiWFuX|NoW#=W@TrUoymB8DBcR(l!R_=7Y4o`pHDd;*b2UO@e65gs{nRfIT z0AhOsW)m>M57=01P-5R4HYdHuTF?v%$OQrE^EHAGtG9l(G=-e}u6o!(!OTJ8{$Sph z>Mc&oaQm%`m%r?{=>NUH%F{qim1Ta}`%)j?iqTi;m{PceemFVE0Vep#nb8#uSOHg% zfUbFC`>JTU@~X>Oh}^M)*O#d*=qAeTZDp7&S4vCkS+%TOGEe!NZ{61#WT))K&YVvD z7+uA|T}T`M;a|V#2OM~(G2!L6B+S8Fore01xngQhz$KaHCt%QTQ(W5sp9X0-dB$TF z_yu~zPA{h6nNsd?zP+6~7>@`hg60ZVE2S=QZ z{J$@!7(K10pzR;V8$XOZ27q?r$hSPQxPt70a#*ev1=w)>KKC2z7VJzg$iGR%vRB47 zdHp6R>t>3rVFV`q{P(GyHpV=w*AoBn%q>88LqPr(RaPZg)daW@uq4OsuI*A2=f*qE;x*iEf@*4h zgl4YEw*=;bR%2F4DE-OZKuV&V)4Hwu?kn6RxR+!$%abf4UMSb_g^cV!ZCmSo&6QJy z%s6D6{duVpI$+VicBE3rU zJ0^1Zpi}X*0iWe)K@x-)>-z%k|31dgFI5BQbl8SzuNV;f`D{o*8HCTMonk5f)brJd$`E&K(-5(1iw6^Ld2wgk-y@%-C5+gh*A z^}WtbyCT2(SS0#{S-*+@{ND$P2oJ1xZzsNF8V`NZ5Dnfwg0EM8kNpC#8q#+bAQxOw zdW6UH?on;uh7Xn4%_FE#Z9~NB?}pxf08CqP^QU97kcP}y-){V!@xE*}G^vVnf$Au> zxv6U17NM02%vr^q-8XnQ$f%s~x}W@aj0nUHi->4{a)l<$sx;||GNee6ByufYPy@38 zv&X%@8LDkdms$Csb2+!X);5ma55P5rReUEqkx#TF%b_ckcK~%gZ$hT@Bbb3LEF95UnGO(?BE08%{ zX8XPF=T_ls@*e)0;1211K>(hMD}M?mZ(IW^bJAt;8O#Z6r?Lpku~vLU!Kr)1`iiqc z#}dH-QAgZXZt%c1^?zUSEst6l&9URD)>oDML$GIQ>gWJs=K`dM!e40n1e~ zxpI$>^cr-UpjDXrc}-qU1an=VLjFg(*Y!7LVPVpgzfP&7=Ei}?Bin`8UvuLBP{U&(TTK_6*V(!+{ZSfCrZwC=rInY}Jws+ohq?*=uk^eoMlXVcHaRx-NUcKv!@6-L0LNl* zsBI~R$M|j895};{tD6snv$J3h>PKoA2;@3~7*5^ixo0LB`u)$D1FL3ALuC2(Y!PeY zP?^H-t#-PrfJiuX_nF6Ns7^KpFZdwq5LdY^OJ%UiMhy5HR{!uj1&w7D%H?| zV+EJnsblX5QNMjJ5wD7-Q!2aLXx_cJ^)(|>*m;Cg88^m#tM8VrG@bMr9 zQB$^A$BJ>XjRjiUfx(VL4fAkv+bFG;0qeE$Fe@?dzg%zk$O$4whSC$~WoZoGaaR)k zt4xvk1|!Yl4-aN0k>DU(egiCx!|9<6@bjk?c>^165vvLWEeTGIVQ06v9~$~7=&1JNR{Kc%+ur-d{LL&`meiXY=#kg{u2KUj=f zhvEF+XL~Z@8*yd=kT6v&_S`FzRQ{2|`N`BpJE+v22@|BW&mNvg2rFHseT1}oE|%f^ zqv=gIyrru6u|;V?M%shd<*!wP=f`SHWS`nJ2}}$y7)Az|%@BH?q-GK8OR4f$F6DBT@-`kg07IV&T>A6f0I~i{4NY?PdFXAWSAC zCV;%0QRP$C7zeu5r7|HHHP|R^h!IQw+oF^-r)KN@Sbq{~!x{loNE{RkZveHX@eeX;$)9#(o62VG5%xDOa-Z&T)qR>7(@Iny zq|q=R;bWEfcPtx*2N2KzloVlJy-vW81@G&Qwl=9w@3|1gdrY;*upq)KHZX{dPRm}SJVzaLDtu~M5r_^3op4}v8@aXTr^}_Bc_;;Q};ky75 zV*^zN$y*%GMcez&I$B%5Ee^zg>6zbzE2EsFX)cf}07?)Uvx+$XMZrE25Xai)`p46Tli})(`4QDigJ1qTO|mfO!ka5- z7Mw0-z^f<0W5vldynFYPUQ_*^?CcbT7y)Sj(EpgL1u5QQ3JSqyQ`H*P0Oa;+z7s-D zK`?nH{vGX5fTox`f7yliJvePWz_Wl+Pm_9sP{Ea#*!r}65^2T zMCi!KHf68u#yXsXtk=;XG$^|w%N*HG3x%UoF(k(_vLy`J4-q1!FqTY7mU5qO=ib-B z{SWRdKX~yPzRPEMKF{)gzaL_dX;;F8^=BgaZpi`rQUQlSbMl(cQgd~Z(uq{UQrz*1 zjK~{X&n+Eq_6-;~(E8=|d9{Eilh&6xJQs_AMj2PA=tOdf zBtzwr4v|<~;qhANnT)Xvt?=x@fQK)h!wcdnL+`)J)YQ#wIB=t`9tC%9iP=MdbEQI- zASA7DHgJD(tyb{HDRWw$YZzacYF}v?No?!UXLvRec1*0+y_)%bWGlIp&MTE+tMqD%VZC|z~p#tg2rVL!7D#%n&0S&`_R_{^+ zGk+^231kp6@{`ZK^&@s&AZqQ<*xl?Ct@4>!u|!VDaptvp3g?-SFXxJL{o7dI&_<+x zb_x>+lDNF)CY9J!vF(7+V-76316h=IXlP)X^6&(~&C9<)SkHEj&(QwLUjV~w%A+8Z zni33gusGAuTk$c3&8;wJM{***iEcEEmA9l_obiRA7wKKAO7(*DH|Hw}InOemnoOsQ zc8vrRg2d{*7_Cst=SRp9QdqOf_u)$|2U$&6A4J#kZ!$3OK1TGkJ+?V+s3dmCMRNDv zGvMy&x^;hmiPA>50SFTkg`gjZC#vi+;vfxI{LAM=OeipuZuO8{PI_^l458vKU0@r9}Z_el%YAV%ATrHJCgr`$gdXWxvN!2wzKE zH8Y|#NgqqC?AiG{4%AvHS^T10s~9}jZS}_<_pL^VF_R5m*>0h3GeR9oGrP;a&ZZSi zI)J*G{6_(86_q{-0!5=4#;_pziV)Yiay5&x3^Ms7AXF@;(F0J4(7_Srj|+f^38ygH z3h6}$4P;V*X=)5TWAj0bJ#QgHpVs{0lgbE7qfJvBmr13J8#hh(`1`h}n}sohi&N}$ zkJ>{4Crhkqj`SL7RK#vIBvcMhDXv@YxR!HTfT%gie_j~Gm+uPuOP;$aeJNYQgpMLY z^$SGEZ0d5~Aq&WxD*k>G zf9+7%`-ZsogL`Z%uWP3(dtV%nWTilDZGXg{Zd|Pif-df3FO?^jHL9Y3BA#rY*`0-3 zS-DVl!k6QpvzZaq>*xX*0p;@@S!P869miVoopKBIcX}PoRc`>_SQ0HJn5cm~rzX^U z;+pb2Rl2BCQ!%@)gVurtXa|ANBHeiUx@yS#@$T^efQ@8=v`Z`GY!ud>_bx=}H|@La z;mqScVAG^8m(hV!xErq}JZmH>6;vJd_T;D6(?Pp&m@LgHZJX56JzF=dj_BR`6z#7E z1zYT-c>5;sRlpHMRL=%qm} zdzxVk7%^Ny4*g>%&PVg$-aYk~9neCmR(jl$HVR+;uHTE(LTmg4L`wT7GOq@I4Wj*J zPJ2^RU5wO8YdL+loOLi>FD*s2wVmOpQ2-Y_1N6q)oPtLs07;e!jILJb@*tFR^Ne=i zZ@X_Nk@Z`021;u_&dLfK*u^VVLvVA-6+6?dBWT6P&%bEr7GcVWJT7o$MHsD=ZY1-K zgiMqo-YrZ-0cBrOSCHG!0}BbB5!XL1^=?VSa{SS{ z$6nX?3)c%gS3P7b`Pr{+U4LWyx^sQNF42KI(+1av-rN$!3=v3L-KEBob+aR|h*Nh+PG2(H>1>m)Jg z6nu+QP}NW*{G&Od6L88&^-23%aM_=|`%o}1bea&pIy{(sXl;X!#U1z?%gbE?;n)uJ zv~QyPRyPlcL-V6`dUwe;Ym8rEVkCdTJ*3 z9Y}+7I~l;VoiAoO`oV8G*qcXXV~P2P;1AT`$TxShGP!3DI8i0WBa z7XxqaT6lc&;?t<76vb$^1Q;xqgX(D(+ztQSEFAVsuvLL{l3Sy}30bQE>Il8(YHr?6#0-whciA3IiY2A02 zYg~OCHEE#7eFYO;<)ipep{}}&2=TQ(S4sI?_Tc8L8`MIxox_>6*ARFiej_IaS#5_k zX0u$A(;%=lfqoQ-Vq-5fSrj-KLIOu$9~ZIeo((}2QuEF8(g+9$crnL{n?<4#1 zH*-dYVFz#AG+;qHmeJL)<@7|C#RvL5?IN6G^vW<&nBncnty_#uG*x+*5f=&Q^B9#8 z^&t)#!b3!m?1+(uO_aVcTGI|07I*M_ky=(L1P$&`EGh#9Iw)(zLt><`qccBuY4=p^ zT6wzKfsDEU@t*ot1(RHo&c>#fh$fl+)6-so114WIMlIYZJMMl(rxCe*`A1N=^YIkL ztBBb&U*n^`bWOPdIR!%q5()tM*ZaHa4^?1>+(CPm4qX?&$zwp}?+L5Ruk)=^Mi~M2-rqQ+h_5o^SQr;97@( zRDx&Ac~ObYRn@RmCj6+vyKl8zYCvdG!p!1lLhLncxC8*m(+NW^Me(*uWZ94v>sRg1 zSaw{7$1sk8(MGv{sE~%bHdu1Nd3`tzBY*i_T;A9@!9z1T?`;$jFhn?>R61@KrD=C1Qg(ms{z7*>*6|-OB}>8HE}lYZz=ii8XxLDb%+Eg-yK|03MSvy~QXW$5finJa zlm3ojwjov8B7}Ed3*sA~Q#Wci-7jw7k^fv%($1V$FFWkhF|t@!eS7Ncp_%M2x_Ubf z2{Q1Isq<6ETFxyAme%Y6GO6$d1SnJ3!! zuXF8M?UTA{s<@o!>ib9KuKUWa-p!XD;fh<`qko)pv)N(3$Gu^FVi!f&-yMhq^0x$S z;DMgv-XuPyq2;XP;DSu?*)042Ib1>z=!OsB=9(l>Fv~q~!A450?8!cS23<0JU0lgQ z1+~Ta=X-8gY<9*_Ium!i9*?0l442htKsv~@M-qRQxv6cU{EM%bX#yYF(VR7E1-58a z*NEcqdg7KO&);T25#Yw5p^KI)G0cI!4}mb3J}0)B*6|d3bumZ(t_o_cq-TgtDBdr; zp{{*Ca(OTSe@UL!b-^Y}E8b#u4X+=wI7>mSV+dLJ;y+kpw}&-6I`s-L zAAUpo)0uV}G}r{9cj1<&4=ilWV*dBf{Gsn!hQlWmb%!m#B4@VBe zvdPfYRo}TGkPi>D^SJ1_O`O}g1ChV|6A|`uH#OVtAHzSvhmi^J8hh<3w^bznbA#`% x_lJfMtbK?7h_za{taSEn#ceE literal 45112 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 - - - - - - - + - - + + + - - - - - - + + - - - + + - - - + + + + + + + From 2d1a111a22b2dad098c6b44218225718fcea1bee Mon Sep 17 00:00:00 2001 From: Michael Baumgartner Date: Sat, 2 May 2020 17:51:05 +0000 Subject: [PATCH 38/46] autopep8 fix --- rising/transforms/affine.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rising/transforms/affine.py b/rising/transforms/affine.py index 0722eabb..f44cf634 100644 --- a/rising/transforms/affine.py +++ b/rising/transforms/affine.py @@ -588,6 +588,7 @@ class Scale(BaseAffine): The transformation will be applied to all the dict-entries specified in :attr:`keys`. """ + def __init__(self, scale: AffineParamType, keys: Sequence = ('data',), From 5d8d1592cecf46cf9d5eafcf93ad96f03c07aaa0 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 20:01:08 +0200 Subject: [PATCH 39/46] update logos and requirements --- docs/source/images/logo/rising_logo.png | Bin 0 -> 45261 bytes docs/source/images/logo/rising_logo.svg | 30 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 docs/source/images/logo/rising_logo.png create mode 100644 docs/source/images/logo/rising_logo.svg diff --git a/docs/source/images/logo/rising_logo.png b/docs/source/images/logo/rising_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..490496e5633d3118d7da9bcb6872faa4f1aeb936 GIT binary patch literal 45261 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 x_lJfMtbK?7h_za{taSEn#ceE literal 0 HcmV?d00001 diff --git a/docs/source/images/logo/rising_logo.svg b/docs/source/images/logo/rising_logo.svg new file mode 100644 index 00000000..a0015893 --- /dev/null +++ b/docs/source/images/logo/rising_logo.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 177fef7e23f2c03a9c96e3dcdedbc0ba2f97c8a1 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 20:04:19 +0200 Subject: [PATCH 40/46] Add Empty Package Docs --- docs/source/get_started.rst | 2 -- docs/source/getting_started.rst | 2 +- docs/source/index.rst | 9 ++++----- docs/source/loading.rst | 2 ++ docs/source/ops.rst | 2 ++ docs/source/ops.rst.py | 0 docs/source/transforms.rst | 2 ++ docs/source/utils.rst | 2 ++ 8 files changed, 13 insertions(+), 8 deletions(-) delete mode 100644 docs/source/get_started.rst create mode 100644 docs/source/ops.rst delete mode 100644 docs/source/ops.rst.py diff --git a/docs/source/get_started.rst b/docs/source/get_started.rst deleted file mode 100644 index c3ce665d..00000000 --- a/docs/source/get_started.rst +++ /dev/null @@ -1,2 +0,0 @@ -Getting Started -=============== \ No newline at end of file diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index c3ce665d..3ce1d78a 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -1,2 +1,2 @@ Getting Started -=============== \ No newline at end of file +=============== diff --git a/docs/source/index.rst b/docs/source/index.rst index b5d6697f..35c69e71 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -13,11 +13,10 @@ rising Documentation :name: docs :caption: Python API - loading - ops - transforms - utils - interface + loading + ops + transforms + utils .. toctree:: :maxdepth: 1 diff --git a/docs/source/loading.rst b/docs/source/loading.rst index e69de29b..a6fc8336 100644 --- a/docs/source/loading.rst +++ b/docs/source/loading.rst @@ -0,0 +1,2 @@ +Loading +======= diff --git a/docs/source/ops.rst b/docs/source/ops.rst new file mode 100644 index 00000000..9907391e --- /dev/null +++ b/docs/source/ops.rst @@ -0,0 +1,2 @@ +Ops +=== diff --git a/docs/source/ops.rst.py b/docs/source/ops.rst.py deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index e69de29b..aa9ec329 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -0,0 +1,2 @@ +Transforms +========== diff --git a/docs/source/utils.rst b/docs/source/utils.rst index e69de29b..a8130f0f 100644 --- a/docs/source/utils.rst +++ b/docs/source/utils.rst @@ -0,0 +1,2 @@ +Utilities +========= From 9ff9d357bcd81e121f08da3049c3373da08ecbf0 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 20:31:52 +0200 Subject: [PATCH 41/46] optimize imports --- rising/loading/dataset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rising/loading/dataset.py b/rising/loading/dataset.py index d0fb125d..ebdf0a3e 100644 --- a/rising/loading/dataset.py +++ b/rising/loading/dataset.py @@ -2,12 +2,12 @@ import os import pathlib from functools import partial -from multiprocessing import cpu_count, Pool as MPPool -from typing import Any, Sequence, Callable, Union, List, Hashable, Dict, Iterator, Generator, Optional -from warnings import warn +from multiprocessing import cpu_count +from typing import Any, Sequence, Callable, Union, List, Iterator, Generator, Optional try: import dill + DILL_AVAILABLE = True except ImportError: DILL_AVAILABLE = False From 5556e66444007c836224fa6ef2dccf31bdfd0b63 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 20:32:04 +0200 Subject: [PATCH 42/46] add loading docs --- docs/source/loading.rst | 93 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/docs/source/loading.rst b/docs/source/loading.rst index a6fc8336..0bdd8598 100644 --- a/docs/source/loading.rst +++ b/docs/source/loading.rst @@ -1,2 +1,91 @@ -Loading -======= +.. role:: hidden + :class: hidden-section + +Loading - rising.loading +======================== + +.. automodule:: rising.loading +.. currentmodule:: rising.loading + +DataLoader +---------- + +:hidden:`DataLoader` +~~~~~~~~~~~~~~~~~~~~ + +.. currentmodule:: rising.loading.loader + +.. autoclass:: DataLoader + :members: + :undoc-members: + :show-inheritance: + +:hidden:`default_transform_call` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunc:: default_transform_call + +:hidden:`BatchTransformer` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: BatchTransformer + :members: + :undoc-members: + :show-inheritance: + +:hidden:`patch_worker_init_fn` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunc:: patch_worker_init_fn + +:hidden:`patch_collate_fn` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunc:: patch_collate_fn + +Dataset +------- + +:hidden:`Dataset` +~~~~~~~~~~~~~~~~~ + +.. currentmodule:: rising.loading.dataset + +.. autoclass:: Dataset + :members: + :undoc-members: + :show-inheritance: + +:hidden:`AsyncDataset` +~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: AsyncDataset + :members: + :undoc-members: + :show-inheritance: + +:hidden:`dill_helper` +~~~~~~~~~~~~~~~~~~~~~ + +.. autofunc:: dill_hepler + +:hidden:`load_async` +~~~~~~~~~~~~~~~~~~~~ + +.. autofunc:: load_async + +Collation +--------- + +:hidden:`numpy_collate` +~~~~~~~~~~~~~~~~~~~~~~~ + +.. currentmodule:: rising.loading.collate + +.. autofunc:: numpy_collate + +:hidden:`do_nothing_collate` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunc:: do_nothing_collate + From 6a9eeaeefa28d5a5f466cbe235749678d2c3c063 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 22:23:36 +0200 Subject: [PATCH 43/46] finish loading docs --- docs/source/conf.py | 35 +++++++++++++++++------------------ docs/source/index.rst | 6 ------ docs/source/loading.rst | 15 ++++++++------- rising/loading/__init__.py | 25 +++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 6c356c92..87f80fb9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,7 +12,6 @@ # 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. -import glob import inspect import os import shutil @@ -255,23 +254,23 @@ ] -def run_apidoc(_): - for pkg in PACKAGES: - argv = ['-e', '-o', PATH_HERE, os.path.join(PATH_HERE, PATH_ROOT, pkg), - '**/test_*', '--force', '--private', '--module-first'] - try: - # Sphinx 1.7+ - from sphinx.ext import apidoc - apidoc.main(argv) - except ImportError: - # Sphinx 1.6 (and earlier) - from sphinx import apidoc - argv.insert(0, apidoc.__file__) - apidoc.main(argv) - - -def setup(app): - app.connect('builder-inited', run_apidoc) +# def run_apidoc(_): +# for pkg in PACKAGES: +# argv = ['-e', '-o', PATH_HERE, os.path.join(PATH_HERE, PATH_ROOT, pkg), +# '**/test_*', '--force', '--private', '--module-first'] +# try: +# # Sphinx 1.7+ +# from sphinx.ext import apidoc +# apidoc.main(argv) +# except ImportError: +# # Sphinx 1.6 (and earlier) +# from sphinx import apidoc +# argv.insert(0, apidoc.__file__) +# apidoc.main(argv) +# +# +# def setup(app): +# app.connect('builder-inited', run_apidoc) # copy all notebooks to local folder diff --git a/docs/source/index.rst b/docs/source/index.rst index 35c69e71..e650b60b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -39,9 +39,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - -.. This is here to make sphinx aware of the modules but not throw an error/warning -.. toctree:: - :hidden: - - modules diff --git a/docs/source/loading.rst b/docs/source/loading.rst index 0bdd8598..8daebf8a 100644 --- a/docs/source/loading.rst +++ b/docs/source/loading.rst @@ -5,6 +5,7 @@ Loading - rising.loading ======================== .. automodule:: rising.loading + .. currentmodule:: rising.loading DataLoader @@ -23,7 +24,7 @@ DataLoader :hidden:`default_transform_call` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunc:: default_transform_call +.. autofunction:: default_transform_call :hidden:`BatchTransformer` ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -36,12 +37,12 @@ DataLoader :hidden:`patch_worker_init_fn` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunc:: patch_worker_init_fn +.. autofunction:: patch_worker_init_fn :hidden:`patch_collate_fn` ~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunc:: patch_collate_fn +.. autofunction:: patch_collate_fn Dataset ------- @@ -67,12 +68,12 @@ Dataset :hidden:`dill_helper` ~~~~~~~~~~~~~~~~~~~~~ -.. autofunc:: dill_hepler +.. autofunction:: dill_helper :hidden:`load_async` ~~~~~~~~~~~~~~~~~~~~ -.. autofunc:: load_async +.. autofunction:: load_async Collation --------- @@ -82,10 +83,10 @@ Collation .. currentmodule:: rising.loading.collate -.. autofunc:: numpy_collate +.. autofunction:: numpy_collate :hidden:`do_nothing_collate` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunc:: do_nothing_collate +.. autofunction:: do_nothing_collate diff --git a/rising/loading/__init__.py b/rising/loading/__init__.py index 7eec160e..0abb4389 100644 --- a/rising/loading/__init__.py +++ b/rising/loading/__init__.py @@ -1,3 +1,28 @@ +""" +``rising.loading`` provides an alternative :class:`DataLoader` that extends +:class:`torch.utils.data.DataLoader` by the following: + +* Seeding of Numpy in each worker process: The seed is generated by numpy in + the main process before starting the workers. For reproducibility numpy must + be seeded in the main process. + +* Per-Sample Transforms outside the dataset (optional with pseudo + batch dimension if the transforms require it). + Will be executed within the spawned worker processes before batching. + +* Batched Transforms for better performance. + Will be executed within the worker processes after batching. + +* Batched GPU-Transforms. Will be executed after syncing results back to + main process (i.e. as last transforms) to avoid multiple CUDA initializations. + +Furthermore it also provides a :class:`Dataset` (based on +:class:`torch.utils.data.Dataset`)that can create subsets from itself by +given indices and an :class:`AsyncDataset` as well as different options for +collation. + +""" + from rising.loading.collate import numpy_collate from rising.loading.dataset import Dataset, AsyncDataset from rising.loading.loader import DataLoader, default_transform_call From c3ee5d716138f28efe084c3f4f484ec663fb4674 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 22:53:54 +0200 Subject: [PATCH 44/46] Document tensor ops --- docs/source/ops.rst | 26 ++++++++++++++++++++++++-- rising/ops/__init__.py | 4 ++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/source/ops.rst b/docs/source/ops.rst index 9907391e..920289bb 100644 --- a/docs/source/ops.rst +++ b/docs/source/ops.rst @@ -1,2 +1,24 @@ -Ops -=== +.. role:: hidden + :class: hidden-section + +Operators +========= + +.. automodule:: rising.ops + +.. currentmodule:: rising.ops + +On Tensors +---------- + +:hidden:`torch_one_hot` +~~~~~~~~~~~~~~~~~~~~~~~ + +.. currentmodule:: rising.ops.tensor + +.. autofunction:: torch_one_hot + +:hidden:`np_one_hot` +~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: np_one_hot diff --git a/rising/ops/__init__.py b/rising/ops/__init__.py index 73916e74..29e550ed 100644 --- a/rising/ops/__init__.py +++ b/rising/ops/__init__.py @@ -1 +1,5 @@ +""" +Provides Operators working on single tensors. +""" + from .tensor import torch_one_hot, np_one_hot From 3c9d932642dc2c0f968c8c9cfb5f0819a752a8e1 Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sat, 2 May 2020 23:36:01 +0200 Subject: [PATCH 45/46] Add docs for modular transforms --- docs/source/transforms.rst | 494 +++++++++++++++++++++++++++++++++- rising/transforms/__init__.py | 5 + 2 files changed, 497 insertions(+), 2 deletions(-) diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index aa9ec329..ff798d37 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -1,2 +1,492 @@ -Transforms -========== +.. role:: hidden + :class: hidden-section + +Data Transformations - rising.transforms +======================================== + +.. automodule:: rising.transforms + +.. currentmodule:: rising.transforms + +Module Interface +---------------- + +Transformation Base Classes +*************************** + +:hidden:`AbstractTransform` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. currentmodule:: rising.transforms.abstract + +.. autoclass:: AbstractTransform + :members: + :undoc-members: + :show-inheritance: + +:hidden:`BaseTransform` +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: BaseTransform + :members: + :undoc-members: + :show-inheritance: + +:hidden:`PerSampleTransform` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: PerSampleTransform + :members: + :undoc-members: + :show-inheritance: + +:hidden:`PerChannelTransform` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: PerChannelTransform + :members: + :undoc-members: + :show-inheritance: + +:hidden:`RandomDimsTransform` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: RandomDimsTransform + :members: + :undoc-members: + :show-inheritance: + +:hidden:`RandomProcess` +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: RandomProcess + :members: + :undoc-members: + :show-inheritance: + +Compose +******* + +.. currentmodule:: rising.transforms.compose + +:hidden:`Compose` +~~~~~~~~~~~~~~~~~ + +.. autoclass:: Compose + :members: + :undoc-members: + :show_inheritance: + +:hidden:`DropoutCompose` +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: DropoutCompose + :members: + :undoc-members: + :show_inheritance: + +:hidden:`dict_call` +~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: dict_call + +Affine Transforms +**************** + +.. py:currentmodule:: rising.transforms.affine + +:hidden:`Affine` +~~~~~~~~~~~~~~~ + +.. autoclass:: Affine + :members: + :undoc-members: + :show-inheritance: + +:hidden:`StackedAffine` +~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: StackedAffine + :members: + :undoc-members: + :show-inheritance: + +:hidden:`BaseAffine` +~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: BaseAffine + :members: + :undoc-members: + :show-inheritance: + +:hidden:`Rotate` +~~~~~~~~~~~~~~~~ + +.. autoclass:: Rotate + :members: + :undoc-members: + :show-inheritance: + +:hidden:`Translate` +~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: Translate + :members: + :undoc-members: + :show-inheritance: + +:hidden:`Scale` +~~~~~~~~~~~~~~~ + +.. autoclass:: Scale + :members: + :undoc-members: + :show-inheritance: + +:hidden:`Resize` +~~~~~~~~~~~~~~~~ + +.. autoclass:: Resize + :members: + :undoc-members: + :show-inheritance: + +Channel Transforms +****************** + +.. currentmodule:: rising.transforms.channel + +:hidden:`OneHot` +~~~~~~~~~~~~~~~~ + +.. autoclass:: OneHot + :members: + :undoc-members: + :show_inheritance: + +Cropping Transforms +******************* + +.. currentmodule:: rising.transforms.crop + +:hidden:`CenterCrop` +~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: CenterCrop + :members: + :undoc-members: + :show_inheritance: + +:hidden:`RandomCrop` +~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: RandomCrop + :members: + :undoc-members: + :show_inheritance: + +:hidden:`CenterCropRandomSize` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: CenterCropRandomSize + :members: + :undoc-members: + :show_inheritance: + +:hidden:`RandomCropRandomSize` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: RandomCropRandomSize + :members: + :undoc-members: + :show_inheritance: + +Format Transforms +***************** + +.. currentmodule:: rising.transforms.format + +:hidden:`MapToSeq` +~~~~~~~~~~~~~~~~~~ + +.. autoclass:: MapToSeq + :members: + :undoc-members: + :show_inheritance: + +:hidden:`SeqToMap` +~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SeqToMap + :members: + :undoc-members: + :show_inheritance: + +Intensity Transforms +******************** + +.. currentmodule:: rising.transforms.format + +:hidden:`Clamp` +~~~~~~~~~~~~~~~ + +.. autoclass:: Clamp + :members: + :undoc-members: + :show_inheritance: + +:hidden:`NormRange` +~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: NormRange + :members: + :undoc-members: + :show_inheritance: + +:hidden:`NormMinMax` +~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: NormMinMax + :members: + :undoc-members: + :show_inheritance: + +:hidden:`NormZeroMeanUnitStd` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: NormZeroMeanUnitStd + :members: + :undoc-members: + :show_inheritance: + +:hidden:`NormMeanStd` +~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: NormMeanStd + :members: + :undoc-members: + :show_inheritance: + +:hidden:`Noise` +~~~~~~~~~~~~~~~ + +.. autoclass:: Noise + :members: + :undoc-members: + :show_inheritance: + +:hidden:`GaussianNoise` +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: GaussianNoise + :members: + :undoc-members: + :show_inheritance: + +:hidden:`ExponentialNoise` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: ExponentialNoise + :members: + :undoc-members: + :show_inheritance: + +:hidden:`GammaCorrection` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: GammaCorrection + :members: + :undoc-members: + :show_inheritance: + +:hidden:`RandomValuePerChannel` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: RandomValuePerChannel + :members: + :undoc-members: + :show_inheritance: + +:hidden:`RandomAddValue` +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: RandomAddValue + :members: + :undoc-members: + :show_inheritance: + +:hidden:`RandomScaleValue` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: RandomScaleValue + :members: + :undoc-members: + :show_inheritance: + +Kernel Transforms +***************** + +.. currentmodule:: rising.transforms.kernel + +:hidden:`KernelTransform` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: KernelTransform + :members: + :undoc-members: + :show_inheritance: + +:hidden:`GaussianSmoothing` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: GaussianSmoothing + :members: + :undoc-members: + :show_inheritance: + +Spatial Transforms +****************** + +.. currentmodule:: rising.transforms.spatial + +:hidden:`Mirror` +~~~~~~~~~~~~~~~~ + +.. autoclass:: Mirror + :members: + :undoc-members: + :show_inheritance: + +:hidden:`Rot90` +~~~~~~~~~~~~~~~ + +.. autoclass:: Rot90 + :members: + :undoc-members: + :show_inheritance: + +:hidden:`Resize` +~~~~~~~~~~~~~~~~ + +.. autoclass:: Resize + :members: + :undoc-members: + :show_inheritance: + +:hidden:`Zoom` +~~~~~~~~~~~~~~ + +.. autoclass:: Zoom + :members: + :undoc-members: + :show_inheritance: + +:hidden:`ProgressiveResize` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: ProgressiveResize + :members: + :undoc-members: + :show_inheritance: + +:hidden:`SizeStepScheduler` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SizeStepScheduler + :members: + :undoc-members: + :show_inheritance: + +Tensor Transforms +***************** + .. py:currentmodule:: rising.transforms.tensor + +:hidden:`ToTensor` +~~~~~~~~~~~~~~~~~~ + +.. autoclass:: ToTensor + :members: + :undoc-members: + :show_inheritance: + +:hidden:`ToDevice` +~~~~~~~~~~~~~~~~~~ + +.. autoclass:: ToDevice + :members: + :undoc-members: + :show_inheritance: + +:hidden:`TensorOp` +~~~~~~~~~~~~~~~~~~ + +.. autoclass:: TensorOp + :members: + :undoc-members: + :show_inheritance: + +:hidden:`Permute` +~~~~~~~~~~~~~~~~~ + +.. autoclass:: Permute + :members: + :undoc-members: + :show_inheritance: + +Utility Transforms +****************** + +.. currentmodule:: rising.transforms.utility + +:hidden:`DoNothing` +~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: DoNothing + :members: + :undoc-members: + :show_inheritance: + +:hidden:`SegToBox` +~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SegToBox + :members: + :undoc-members: + :show_inheritance: + +:hidden:`BoxToSeg` +~~~~~~~~~~~~~~~~~~ + +.. autoclass:: BoxToSeg + :members: + :undoc-members: + :show_inheritance: + +:hidden:`InstanceToSemantic` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: InstanceToSemantic + :members: + :undoc-members: + :show_inheritance: + +:hidden:`PopKeys` +~~~~~~~~~~~~~~~~~ + +.. autoclass:: PopKeys + :members: + :undoc-members: + :show_inheritance: + +:hidden:`FilterKeys` +~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: FilterKeys + :members: + :undoc-members: + :show_inheritance: + +Functional Interface +-------------------- + +.. automodule:: rising.transforms.functional \ No newline at end of file diff --git a/rising/transforms/__init__.py b/rising/transforms/__init__.py index 026fe35f..5cac2804 100644 --- a/rising/transforms/__init__.py +++ b/rising/transforms/__init__.py @@ -1,3 +1,8 @@ +""" +Provides the Augmentations and Transforms used by the +:class:`rising.loading.DataLoader`. +""" + from rising.transforms.abstract import * from rising.transforms.channel import * from rising.transforms.compose import * From 078dea7b4e799d55688a13e32ec8c925a239eb6e Mon Sep 17 00:00:00 2001 From: Justus Schock Date: Sun, 3 May 2020 12:37:29 +0200 Subject: [PATCH 46/46] finish initial docs --- docs/build_docs.sh | 2 +- docs/requirements.txt | 6 +- docs/source/_templates/layout.html | 39 ++++ docs/source/_templates_stable/layout.html | 35 +++ docs/source/conf.py | 80 ++++++- docs/source/index.rst | 8 +- docs/source/loading.rst | 16 +- docs/source/ops.rst | 8 +- docs/source/transforms.functional.rst | 234 ++++++++++++++++++++ docs/source/transforms.rst | 230 ++++++++++--------- docs/source/utils.rst | 68 +++++- rising/transforms/__init__.py | 15 ++ rising/transforms/functional/__init__.py | 16 ++ rising/transforms/functional/affine.py | 1 + rising/transforms/functional/spatial.py | 12 +- rising/transforms/spatial.py | 14 +- rising/utils/checktype.py | 5 +- tests/transforms/functional/test_spatial.py | 2 +- tests/transforms/test_spatial_transforms.py | 6 +- 19 files changed, 647 insertions(+), 150 deletions(-) create mode 100644 docs/source/_templates/layout.html create mode 100644 docs/source/_templates_stable/layout.html create mode 100644 docs/source/transforms.functional.rst diff --git a/docs/build_docs.sh b/docs/build_docs.sh index 609f4a8e..9474626f 100644 --- a/docs/build_docs.sh +++ b/docs/build_docs.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -make clean ; make html --debug --jobs 2 SPHINXOPTS="-W" \ No newline at end of file +make clean ; make html --debug --jobs 2 SPHINXOPTS="-W"; make html --debug --jobs 2 SPHINXOPTS="-W" \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt index 070e72ba..6059b679 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,12 +1,12 @@ sphinx>=2.0, <3.0 recommonmark # fails with badges -m2r # fails with multi-line text nbsphinx pandoc docutils sphinxcontrib-fulltoc sphinxcontrib-mockautodoc git+https://github.com/PhoenixDL/rising_sphinx_theme.git -# pip_shims sphinx-autodoc-typehints -sphinx-paramlinks<0.4.0 \ No newline at end of file +sphinx-paramlinks<0.4.0 +sphinxcontrib.katex +javasphinx \ No newline at end of file diff --git a/docs/source/_templates/layout.html b/docs/source/_templates/layout.html new file mode 100644 index 00000000..0686497a --- /dev/null +++ b/docs/source/_templates/layout.html @@ -0,0 +1,39 @@ +{% extends "!layout.html" %} + + +{% block menu %} + +{{ super() }} +{% endblock %} + +{% block footer %} +{{ super() }} + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/docs/source/_templates_stable/layout.html b/docs/source/_templates_stable/layout.html new file mode 100644 index 00000000..d971396d --- /dev/null +++ b/docs/source/_templates_stable/layout.html @@ -0,0 +1,35 @@ + +{% extends "!layout.html" %} + + +{% block menu %} + +{{ super() }} +{% endblock %} + +{% block footer %} +{{ super() }} + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 87f80fb9..8f3f4ac1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -58,6 +58,8 @@ # The full version, including alpha/beta/rc tags release = rising.__version__ + +IS_REALESE = not ('+' in version or 'dirty' in version or len(version.split('.')) > 3) # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. @@ -67,10 +69,10 @@ # 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', - # 'sphinxcontrib.mockautodoc', # raises error: directive 'automodule' is already registered ... - # 'sphinxcontrib.fulltoc', # breaks pytorch-theme with unexpected kw argument 'titles_only' + 'sphinx.ext.autosummary', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', @@ -78,17 +80,25 @@ 'sphinx.ext.linkcode', 'sphinx.ext.autosummary', 'sphinx.ext.napoleon', + 'sphinx.ext.viewcode', + 'sphinxcontrib.katex', 'recommonmark', 'sphinx.ext.autosectionlabel', - # 'm2r', 'nbsphinx', 'sphinx_autodoc_typehints', 'sphinx_paramlinks', + 'javasphinx' ] +katex_prerender = True + +napoleon_use_ivar = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] +if IS_REALESE: + templates_path = ['_templates_stable'] + # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # @@ -118,7 +128,7 @@ ] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = None +pygments_style = 'sphinx' # -- Options for HTML output ------------------------------------------------- @@ -141,7 +151,7 @@ 'canonical_url': rising.__homepage__, 'collapse_navigation': False, 'display_version': True, - 'logo_only': False, + 'logo_only': True, } html_logo = 'images/logo/rising_logo.svg' @@ -149,7 +159,7 @@ # 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 = ['images', '_templates'] +html_static_path = ['images'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -242,6 +252,9 @@ # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True +# Disable docstring inheritance +autodoc_inherit_docstrings = True + # https://github.com/rtfd/readthedocs.org/issues/1139 # I use sphinx-apidoc to auto-generate API documentation for my project. # Right now I have to commit these auto-generated files to my repository @@ -253,6 +266,17 @@ rising.__name__, ] +# -- A patch that prevents Sphinx from cross-referencing ivar tags ------- +# See http://stackoverflow.com/a/41184353/3343043 + +from docutils import nodes +from sphinx.util.docfields import TypedField +from sphinx import addnodes +import sphinx.ext.doctest + +# Without this, doctest adds any example with a `>>>` as a test +doctest_test_doctest_blocks = '' +doctest_default_flags = sphinx.ext.doctest.doctest.ELLIPSIS # def run_apidoc(_): # for pkg in PACKAGES: @@ -383,3 +407,47 @@ def find_source(): # Useful for avoiding ambiguity when the same section heading appears in different documents. # http://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html autosectionlabel_prefix_document = True + +def patched_make_field(self, types, domain, items, **kw): + # `kw` catches `env=None` needed for newer sphinx while maintaining + # backwards compatibility when passed along further down! + + # type: (List, unicode, Tuple) -> nodes.field + def handle_item(fieldarg, content): + par = nodes.paragraph() + par += addnodes.literal_strong('', fieldarg) # Patch: this line added + # par.extend(self.make_xrefs(self.rolename, domain, fieldarg, + # addnodes.literal_strong)) + if fieldarg in types: + par += nodes.Text(' (') + # NOTE: using .pop() here to prevent a single type node to be + # inserted twice into the doctree, which leads to + # inconsistencies later when references are resolved + fieldtype = types.pop(fieldarg) + if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text): + typename = u''.join(n.astext() for n in fieldtype) + typename = typename.replace('int', 'python:int') + typename = typename.replace('long', 'python:long') + typename = typename.replace('float', 'python:float') + typename = typename.replace('type', 'python:type') + par.extend(self.make_xrefs(self.typerolename, domain, typename, + addnodes.literal_emphasis, **kw)) + else: + par += fieldtype + par += nodes.Text(')') + par += nodes.Text(' -- ') + par += content + return par + + fieldname = nodes.field_name('', self.label) + if len(items) == 1 and self.can_collapse: + fieldarg, content = items[0] + bodynode = handle_item(fieldarg, content) + else: + bodynode = self.list_type() + for fieldarg, content in items: + bodynode += nodes.list_item('', handle_item(fieldarg, content)) + fieldbody = nodes.field_body('', bodynode) + return nodes.field('', fieldname, fieldbody) + +TypedField.make_field = patched_make_field diff --git a/docs/source/index.rst b/docs/source/index.rst index e650b60b..e92a215c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,6 +1,11 @@ +:github_url: https://github.com/PhoenixDL/rising + rising Documentation ==================== +``rising`` is a highly performant, ``PyTorch`` only, framework for efficient data augmentation with native +support for volumetric data + .. toctree:: :maxdepth: 2 :name: introduction @@ -9,13 +14,14 @@ rising Documentation getting_started .. toctree:: - :maxdepth: 2 + :maxdepth: 3 :name: docs :caption: Python API loading ops transforms + transforms.functional utils .. toctree:: diff --git a/docs/source/loading.rst b/docs/source/loading.rst index 8daebf8a..1c5b7188 100644 --- a/docs/source/loading.rst +++ b/docs/source/loading.rst @@ -1,15 +1,17 @@ .. role:: hidden :class: hidden-section -Loading - rising.loading -======================== +rising.loading +=============== .. automodule:: rising.loading .. currentmodule:: rising.loading DataLoader ----------- +----------- + +.. automodule:: rising.loading.loader :hidden:`DataLoader` ~~~~~~~~~~~~~~~~~~~~ @@ -45,7 +47,9 @@ DataLoader .. autofunction:: patch_collate_fn Dataset -------- +----------- + +.. automodule:: rising.loading.dataset :hidden:`Dataset` ~~~~~~~~~~~~~~~~~ @@ -76,7 +80,9 @@ Dataset .. autofunction:: load_async Collation ---------- +----------- + +.. automodule:: rising.loading.collate :hidden:`numpy_collate` ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/ops.rst b/docs/source/ops.rst index 920289bb..c534a378 100644 --- a/docs/source/ops.rst +++ b/docs/source/ops.rst @@ -1,15 +1,15 @@ .. role:: hidden :class: hidden-section -Operators -========= +rising.ops +========================= .. automodule:: rising.ops .. currentmodule:: rising.ops On Tensors ----------- +------------------------ :hidden:`torch_one_hot` ~~~~~~~~~~~~~~~~~~~~~~~ @@ -19,6 +19,6 @@ On Tensors .. autofunction:: torch_one_hot :hidden:`np_one_hot` -~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~ .. autofunction:: np_one_hot diff --git a/docs/source/transforms.functional.rst b/docs/source/transforms.functional.rst new file mode 100644 index 00000000..097ae660 --- /dev/null +++ b/docs/source/transforms.functional.rst @@ -0,0 +1,234 @@ +.. role:: hidden + :class: hidden-section + +rising.transforms.functional +----------------------------------------------------------- + +.. automodule:: rising.transforms.functional + +Affine Transforms +********************************************************** + +.. automodule:: rising.transforms.functional.affine + +.. currentmodule:: rising.transforms.functional.affine + +:hidden:`affine_image_transform` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: affine_image_transform + +:hidden:`affine_point_transform` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: affine_point_transform + +:hidden:`parametrize_matrix` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: parametrize_matrix + +:hidden:`create_rotation` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: create_rotation + +:hidden:`create_rotation_2d` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: create_rotation_2d + +:hidden:`create_rotation_3d` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: create_rotation_3d + +:hidden:`create_rotation_3d_0` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: create_rotation_3d_0 + +:hidden:`create_rotation_3d_1` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: create_rotation_3d_1 + +:hidden:`create_rotation_3d_2` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: create_rotation_3d_2 + + +:hidden:`create_scale` +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: create_scale + +:hidden:`create_translation` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: create_translation + +:hidden:`expand_scalar_param` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: expand_scalar_param + +Channel Transforms +********************************************************** + +.. automodule:: rising.transforms.functional.channel + +.. currentmodule:: rising.transforms.functional.channel + +:hidden:`one_hot_batch` +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: one_hot_batch + +Cropping Transforms +******************** + +.. automodule:: rising.transforms.functional.crop + +.. currentmodule:: rising.transforms.functional.crop + +:hidden:`crop` +~~~~~~~~~~~~~~~ + +.. autofunction:: crop + +:hidden:`center_crop` +~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: center_crop + +:hidden:`random_crop` +~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: random_crop + +Device Transforms +********************************************************** + +.. automodule:: rising.transforms.functional.device + +.. currentmodule:: rising.transforms.functional.device + +:hidden:`to_device` +~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: to_device + +Intensity Transforms +********************************************************** + +.. automodule:: rising.transforms.functional.intensity + +.. currentmodule:: rising.transforms.functional.intensity + +:hidden:`norm_range` +~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: norm_range + +:hidden:`norm_min_max` +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: norm_min_max + +:hidden:`norm_zero_mean_unit_std` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: norm_zero_mean_unit_std + +:hidden:`norm_mean_std` +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: norm_mean_std + +:hidden:`add_noise` +~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: add_noise + +:hidden:`add_value` +~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: add_value + +:hidden:`gamma_correction` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: gamma_correction + +:hidden:`scale_by_value` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: scale_by_value + +Spatial Transforms +********************************************************** + +.. automodule:: rising.transforms.functional.spatial + +.. currentmodule:: rising.transforms.functional.spatial + +:hidden:`mirror` +~~~~~~~~~~~~~~~~~ + +.. autofunction:: mirror + +:hidden:`rot90` +~~~~~~~~~~~~~~~~ + +.. autofunction:: rot90 + +:hidden:`resize_native` +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: resize_native + +Tensor Transforms +********************************************************** + +.. automodule:: rising.transforms.functional.tensor + +.. currentmodule:: rising.transforms.functional.tensor + +:hidden:`tensor_op` +~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: tensor_op + +Utility Transforms +********************************************************** + +.. automodule:: rising.transforms.functional.utility + +.. currentmodule:: rising.transforms.functional.utility + +:hidden:`box_to_seg` +~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: box_to_seg + +:hidden:`seg_to_box` +~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: seg_to_box + +:hidden:`instance_to_semantic` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: instance_to_semantic + +:hidden:`pop_keys` +~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: pop_keys + +:hidden:`filter_keys` +~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: filter_keys diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index ff798d37..0ef478ca 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -1,19 +1,18 @@ .. role:: hidden :class: hidden-section -Data Transformations - rising.transforms +rising.transforms ======================================== .. automodule:: rising.transforms .. currentmodule:: rising.transforms -Module Interface ----------------- - Transformation Base Classes *************************** +.. automodule:: rising.transforms.abstract + :hidden:`AbstractTransform` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -64,8 +63,10 @@ Transformation Base Classes :undoc-members: :show-inheritance: -Compose -******* +Compose Transforms +****************** + +.. automodule:: rising.transforms.compose .. currentmodule:: rising.transforms.compose @@ -75,7 +76,7 @@ Compose .. autoclass:: Compose :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`DropoutCompose` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -83,7 +84,7 @@ Compose .. autoclass:: DropoutCompose :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`dict_call` ~~~~~~~~~~~~~~~~~~~ @@ -91,12 +92,14 @@ Compose .. autofunction:: dict_call Affine Transforms -**************** +***************** + +.. automodule:: rising.transforms.affine -.. py:currentmodule:: rising.transforms.affine +.. currentmodule:: rising.transforms.affine :hidden:`Affine` -~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~ .. autoclass:: Affine :members: @@ -104,7 +107,7 @@ Affine Transforms :show-inheritance: :hidden:`StackedAffine` -~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: StackedAffine :members: @@ -112,7 +115,7 @@ Affine Transforms :show-inheritance: :hidden:`BaseAffine` -~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~ .. autoclass:: BaseAffine :members: @@ -120,7 +123,7 @@ Affine Transforms :show-inheritance: :hidden:`Rotate` -~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~ .. autoclass:: Rotate :members: @@ -128,7 +131,7 @@ Affine Transforms :show-inheritance: :hidden:`Translate` -~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~ .. autoclass:: Translate :members: @@ -136,7 +139,7 @@ Affine Transforms :show-inheritance: :hidden:`Scale` -~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~ .. autoclass:: Scale :members: @@ -144,7 +147,7 @@ Affine Transforms :show-inheritance: :hidden:`Resize` -~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~ .. autoclass:: Resize :members: @@ -152,341 +155,354 @@ Affine Transforms :show-inheritance: Channel Transforms -****************** +******************* + +.. automodule:: rising.transforms.channel .. currentmodule:: rising.transforms.channel :hidden:`OneHot` -~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~ .. autoclass:: OneHot :members: :undoc-members: - :show_inheritance: + :show-inheritance: Cropping Transforms -******************* +******************** + +.. automodule:: rising.transforms.crop .. currentmodule:: rising.transforms.crop :hidden:`CenterCrop` -~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: CenterCrop :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`RandomCrop` -~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: RandomCrop :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`CenterCropRandomSize` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: CenterCropRandomSize :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`RandomCropRandomSize` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: RandomCropRandomSize :members: :undoc-members: - :show_inheritance: + :show-inheritance: Format Transforms -***************** +****************** + +.. automodule:: rising.transforms.format .. currentmodule:: rising.transforms.format :hidden:`MapToSeq` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ .. autoclass:: MapToSeq :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`SeqToMap` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ .. autoclass:: SeqToMap :members: :undoc-members: - :show_inheritance: + :show-inheritance: Intensity Transforms -******************** +********************* -.. currentmodule:: rising.transforms.format +.. currentmodule:: rising.transforms.intensity + +.. currentmodule:: rising.transforms.intensity :hidden:`Clamp` -~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~ .. autoclass:: Clamp :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`NormRange` -~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~ .. autoclass:: NormRange :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`NormMinMax` -~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: NormMinMax :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`NormZeroMeanUnitStd` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: NormZeroMeanUnitStd :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`NormMeanStd` -~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: NormMeanStd :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`Noise` -~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~ .. autoclass:: Noise :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`GaussianNoise` -~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: GaussianNoise :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`ExponentialNoise` -~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: ExponentialNoise :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`GammaCorrection` -~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: GammaCorrection :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`RandomValuePerChannel` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: RandomValuePerChannel :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`RandomAddValue` -~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: RandomAddValue :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`RandomScaleValue` -~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: RandomScaleValue :members: :undoc-members: - :show_inheritance: + :show-inheritance: Kernel Transforms -***************** +****************** + +.. currentmodule:: rising.transforms.kernel .. currentmodule:: rising.transforms.kernel :hidden:`KernelTransform` -~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: KernelTransform :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`GaussianSmoothing` -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: GaussianSmoothing :members: :undoc-members: - :show_inheritance: + :show-inheritance: Spatial Transforms -****************** +******************* + +.. automodule:: rising.transforms.spatial .. currentmodule:: rising.transforms.spatial :hidden:`Mirror` -~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~ .. autoclass:: Mirror :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`Rot90` -~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~ .. autoclass:: Rot90 :members: :undoc-members: - :show_inheritance: + :show-inheritance: -:hidden:`Resize` -~~~~~~~~~~~~~~~~ +:hidden:`ResizeNative` +~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: Resize +.. autoclass:: ResizeNative :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`Zoom` -~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~ .. autoclass:: Zoom :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`ProgressiveResize` -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: ProgressiveResize :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`SizeStepScheduler` -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: SizeStepScheduler :members: :undoc-members: - :show_inheritance: + :show-inheritance: Tensor Transforms -***************** - .. py:currentmodule:: rising.transforms.tensor +****************** + +.. automodule:: rising.transforms.tensor + +.. currentmodule:: rising.transforms.tensor :hidden:`ToTensor` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ .. autoclass:: ToTensor :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`ToDevice` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ .. autoclass:: ToDevice :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`TensorOp` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ .. autoclass:: TensorOp :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`Permute` -~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~ .. autoclass:: Permute :members: :undoc-members: - :show_inheritance: + :show-inheritance: Utility Transforms -****************** +******************* + +.. automodule:: rising.transforms.utility .. currentmodule:: rising.transforms.utility :hidden:`DoNothing` -~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~ .. autoclass:: DoNothing :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`SegToBox` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ .. autoclass:: SegToBox :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`BoxToSeg` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ .. autoclass:: BoxToSeg :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`InstanceToSemantic` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: InstanceToSemantic :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`PopKeys` -~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~ .. autoclass:: PopKeys :members: :undoc-members: - :show_inheritance: + :show-inheritance: :hidden:`FilterKeys` -~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: FilterKeys :members: :undoc-members: - :show_inheritance: - -Functional Interface --------------------- + :show-inheritance: -.. automodule:: rising.transforms.functional \ No newline at end of file diff --git a/docs/source/utils.rst b/docs/source/utils.rst index a8130f0f..8db85a3c 100644 --- a/docs/source/utils.rst +++ b/docs/source/utils.rst @@ -1,2 +1,66 @@ -Utilities -========= +.. role:: hidden + :class: hidden-section + +rising.utils +================================================== + +.. automodule:: rising.utils + +Affines +------------------------------------------------- + +.. automodule:: rising.utils.affine + +.. currentmodule:: rising.utils.affine + +:hidden:`points_to_homogeneous` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: points_to_homogeneous + +:hidden:`matrix_to_homogeneous` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: matrix_to_homogeneous + +:hidden:`matrix_to_cartesian` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: matrix_to_cartesian + +:hidden:`points_to_cartesian` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: points_to_cartesian + +:hidden:`matrix_revert_coordinate_order` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: matrix_revert_coordinate_order + +:hidden:`get_batched_eye` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: get_batched_eye + +:hidden:`deg_to_rad` +~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: deg_to_rad + +:hidden:`unit_box` +~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: unit_box + +Type Checks +------------------------------------------ + +.. automodule:: rising.utils.checktype + +.. currentmodule:: rising.utils.checktype + +:hidden:`check_scalar` +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: check_scalar \ No newline at end of file diff --git a/rising/transforms/__init__.py b/rising/transforms/__init__.py index 5cac2804..f84674f2 100644 --- a/rising/transforms/__init__.py +++ b/rising/transforms/__init__.py @@ -1,6 +1,21 @@ """ Provides the Augmentations and Transforms used by the :class:`rising.loading.DataLoader`. + +Implementations include: + +* Transformation Base Classes +* Composed Transforms +* Affine Transforms +* Channel Transforms +* Cropping Transforms +* Device Transforms +* Format Transforms +* Intensity Transforms +* Kernel Transforms +* Spatial Transforms +* Tensor Transforms +* Utility Transforms """ from rising.transforms.abstract import * diff --git a/rising/transforms/functional/__init__.py b/rising/transforms/functional/__init__.py index b1a5aabe..b57be95a 100644 --- a/rising/transforms/functional/__init__.py +++ b/rising/transforms/functional/__init__.py @@ -1,3 +1,19 @@ +""" +Provides a functional interface for transforms +(usually working on single tensors rather then collections thereof). +All transformations are implemented to work on batched tensors. +Implementations include: + +* Affine Transforms +* Channel Transforms +* Cropping Transforms +* Device Transforms +* Intensity Transforms +* Spatial Transforms +* Tensor Transforms +* Utility Transforms +""" + from rising.transforms.functional.crop import * from rising.transforms.functional.device import * from rising.transforms.functional.intensity import * diff --git a/rising/transforms/functional/affine.py b/rising/transforms/functional/affine.py index 0952bebd..65baba91 100644 --- a/rising/transforms/functional/affine.py +++ b/rising/transforms/functional/affine.py @@ -15,6 +15,7 @@ "create_rotation", "create_scale", "create_translation", + "parametrize_matrix" ] AffineParamType = Union[int, float, Sequence, torch.Tensor] diff --git a/rising/transforms/functional/spatial.py b/rising/transforms/functional/spatial.py index ce679dff..94ca7e2c 100644 --- a/rising/transforms/functional/spatial.py +++ b/rising/transforms/functional/spatial.py @@ -3,7 +3,7 @@ from rising.utils import check_scalar -__all__ = ["mirror", "rot90", "resize"] +__all__ = ["mirror", "rot90", "resize_native"] def mirror(data: torch.Tensor, dims: Union[int, Sequence[int]]) -> torch.Tensor: @@ -40,11 +40,11 @@ def rot90(data: torch.Tensor, k: int, dims: Union[int, Sequence[int]]): return torch.rot90(data, k, dims) -def resize(data: torch.Tensor, - size: Optional[Union[int, Sequence[int]]] = None, - scale_factor: Optional[Union[float, Sequence[float]]] = None, - mode: str = 'nearest', align_corners: Optional[bool] = None, - preserve_range: bool = False): +def resize_native(data: torch.Tensor, + size: Optional[Union[int, Sequence[int]]] = None, + scale_factor: Optional[Union[float, Sequence[float]]] = None, + mode: str = 'nearest', align_corners: Optional[bool] = None, + preserve_range: bool = False): """ Down/up-sample sample to either the given :attr:`size` or the given :attr:`scale_factor` diff --git a/rising/transforms/spatial.py b/rising/transforms/spatial.py index 1f79801e..6bc9847a 100644 --- a/rising/transforms/spatial.py +++ b/rising/transforms/spatial.py @@ -10,7 +10,7 @@ from .abstract import RandomDimsTransform, AbstractTransform, BaseTransform, RandomProcess from .functional.spatial import * -__all__ = ["Mirror", "Rot90", "Resize", +__all__ = ["Mirror", "Rot90", "ResizeNative", "Zoom", "ProgressiveResize", "SizeStepScheduler"] scheduler_type = Callable[[int], Union[int, Sequence[int]]] @@ -92,7 +92,7 @@ def dims(self, dims: Sequence): self._permutations = tuple(permutations(dims, 2)) -class Resize(BaseTransform): +class ResizeNative(BaseTransform): """Resize data to given size""" def __init__(self, size: Union[int, Sequence[int]], mode: str = 'nearest', @@ -113,7 +113,7 @@ def __init__(self, size: Union[int, Sequence[int]], mode: str = 'nearest', grad: enable gradient computation inside transformation **kwargs: keyword arguments passed to augment_fn """ - super().__init__(augment_fn=resize, size=size, mode=mode, + super().__init__(augment_fn=resize_native, size=size, mode=mode, align_corners=align_corners, preserve_range=preserve_range, keys=keys, grad=grad, **kwargs) @@ -151,7 +151,7 @@ def __init__(self, random_args: Union[Sequence, Sequence[Sequence]] = (0.75, 1.2 See Also: :func:`random.uniform`, :func:`torch.nn.functional.interpolate` """ - super().__init__(augment_fn=resize, random_args=random_args, + super().__init__(augment_fn=resize_native, random_args=random_args, random_mode=random_mode, mode=mode, align_corners=align_corners, preserve_range=preserve_range, keys=keys, grad=grad, **kwargs) @@ -170,7 +170,7 @@ def forward(self, **data) -> dict: return super().forward(**data) -class ProgressiveResize(Resize): +class ProgressiveResize(ResizeNative): """Resize data to sizes specified by scheduler""" def __init__(self, scheduler: scheduler_type, mode: str = 'nearest', @@ -205,7 +205,7 @@ def __init__(self, scheduler: scheduler_type, mode: str = 'nearest', self.scheduler = scheduler self._step = Value('i', 0) - def reset_step(self) -> Resize: + def reset_step(self) -> ResizeNative: """ Reset step to 0 @@ -216,7 +216,7 @@ def reset_step(self) -> Resize: self._step.value = 0 return self - def increment(self) -> Resize: + def increment(self) -> ResizeNative: """ Increment step by 1 diff --git a/rising/utils/checktype.py b/rising/utils/checktype.py index e1ff5183..44a65f80 100644 --- a/rising/utils/checktype.py +++ b/rising/utils/checktype.py @@ -9,7 +9,4 @@ def check_scalar(x): Returns: True if input is scalar """ - if isinstance(x, (int, float)): - return True - else: - return False + return isinstance(x, (int, float)) diff --git a/tests/transforms/functional/test_spatial.py b/tests/transforms/functional/test_spatial.py index 9feee2bb..7a80cce4 100644 --- a/tests/transforms/functional/test_spatial.py +++ b/tests/transforms/functional/test_spatial.py @@ -26,7 +26,7 @@ def test_rot90(self): self.assertTrue((outp == expected).all()) def test_resize(self): - out = resize(self.batch_2d.float(), (2, 2), preserve_range=True) + out = resize_native(self.batch_2d.float(), (2, 2), preserve_range=True) expected = torch.tensor([[1, 2], [4, 5]]) self.assertTrue((out == expected).all()) diff --git a/tests/transforms/test_spatial_transforms.py b/tests/transforms/test_spatial_transforms.py index 5ed212f9..b5e28ce7 100644 --- a/tests/transforms/test_spatial_transforms.py +++ b/tests/transforms/test_spatial_transforms.py @@ -4,7 +4,7 @@ from tests.transforms import chech_data_preservation from rising.transforms.spatial import * -from rising.transforms.functional.spatial import resize +from rising.transforms.functional.spatial import resize_native from rising.loading import DataLoader @@ -44,7 +44,7 @@ def test_rot90_transform(self): self.assertTrue((outp["data"] == data_orig).all()) def test_resize_transform(self): - trafo = Resize((2, 2)) + trafo = ResizeNative((2, 2)) out = trafo(**self.batch_dict) expected = torch.tensor([[1, 2], [4, 5]]) self.assertTrue((out["data"] == expected).all()) @@ -58,7 +58,7 @@ def test_zoom_transform(self): random.seed(0) out = trafo(**self.batch_dict) - expected = resize(self.batch_dict["data"], mode="nearest", scale_factor=scale_factor) + expected = resize_native(self.batch_dict["data"], mode="nearest", scale_factor=scale_factor) self.assertTrue((out["data"] == expected).all()) def test_progressive_resize(self):