diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/_templates/autosummary/class.rst b/doc/_templates/autosummary/class.rst new file mode 100644 index 0000000..3322b32 --- /dev/null +++ b/doc/_templates/autosummary/class.rst @@ -0,0 +1,10 @@ +{{ fullname | escape | underline }} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :members: + :inherited-members: + +.. minigallery:: {{ fullname }} + :add-heading: diff --git a/doc/_templates/autosummary/function.rst b/doc/_templates/autosummary/function.rst new file mode 100644 index 0000000..cdbecc4 --- /dev/null +++ b/doc/_templates/autosummary/function.rst @@ -0,0 +1,8 @@ +{{ fullname | escape | underline }} + +.. currentmodule:: {{ module }} + +.. autofunction:: {{ objname }} + +.. minigallery:: {{ fullname }} + :add-heading: diff --git a/doc/_templates/autosummary/module.rst b/doc/_templates/autosummary/module.rst new file mode 100644 index 0000000..13a2c27 --- /dev/null +++ b/doc/_templates/autosummary/module.rst @@ -0,0 +1,6 @@ +{{ fullname }} +{{ underline }} + +.. automodule:: {{ fullname }} + :members: + diff --git a/doc/api/index.rst b/doc/api/index.rst new file mode 100644 index 0000000..9eebce1 --- /dev/null +++ b/doc/api/index.rst @@ -0,0 +1,11 @@ +API References +============== + +.. currentmodule:: brainprint + +.. autosummary:: + :toctree: generated/ + + brainprint + asymmetry + surfaces diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..144adb1 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,235 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + + +import inspect +from datetime import date +from importlib import import_module +from typing import Dict, Optional + +# from sphinx_gallery.sorting import FileNameSortKey +import brainprint + +project = "brainprint" +author = "Martin Reuter" +copyright = f"{date.today().year}, {author}" +release = brainprint.__version__ +package = brainprint.__name__ +gh_url = "https://github.com/Deep-MI/BrainPrint" +# -- general configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = "5.0" + +# The document name of the “root” document, that is, the document that contains +# the root toctree directive. +root_doc = "index" + + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.autosectionlabel", + "sphinx.ext.autosummary", + "sphinx.ext.intersphinx", + "sphinx.ext.linkcode", + # "sphinxcontrib.napoleon", + "sphinxcontrib.bibtex", + "sphinx_copybutton", + "sphinx_design", + # "sphinx_gallery.gen_gallery", + "IPython.sphinxext.ipython_console_highlighting", + "numpydoc", + # "sphinx.ext.todo", + # "sphinx.ext.viewcode", + # "myst_parser", +] + + +templates_path = ["_templates"] +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + "**.ipynb_checkpoints", + "tutorials/examples/README.rst", +] + +# Sphinx will warn about all references where the target cannot be found. +# nitpicky = True +nitpicky = False +nitpick_ignore = [] + +# A list of ignored prefixes for module index sorting. +modindex_common_prefix = [f"{package}."] + +# The name of a reST role (builtin or Sphinx extension) to use as the default +# role, that is, for text marked up `like this`. This can be set to 'py:obj' to +# make `filter` a cross-reference to the Python function “filter”. +default_role = "py:obj" + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "furo" +html_static_path = ["_static"] +html_title = project +html_show_sphinx = False + + +# Documentation to change footer icons: +# https://pradyunsg.me/furo/customisation/footer/#changing-footer-icons +html_theme_options = { + "footer_icons": [ + { + "name": "GitHub", + "url": gh_url, + "html": """ + + + + """, + "class": "", + }, + ], +} + + +# -- autosummary ------------------------------------------------------------- +autosummary_generate = True + +# -- autodoc ----------------------------------------------------------------- +autodoc_typehints = "none" +autodoc_member_order = "groupwise" +autodoc_warningiserror = True +autoclass_content = "class" + + +# -- intersphinx ------------------------------------------------------------- +intersphinx_mapping = { + "matplotlib": ("https://matplotlib.org/stable", None), + "mne": ("https://mne.tools/stable/", None), + "numpy": ("https://numpy.org/doc/stable", None), + "pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None), + "python": ("https://docs.python.org/3", None), + "scipy": ("https://docs.scipy.org/doc/scipy", None), + "sklearn": ("https://scikit-learn.org/stable/", None), +} + +intersphinx_timeout = 5 + + +# -- sphinx-issues ----------------------------------------------------------- +issues_github_path = gh_url.split("https://github.com/")[-1] + +# -- autosectionlabels ------------------------------------------------------- +autosectionlabel_prefix_document = True + +# -- sphinxcontrib-bibtex ---------------------------------------------------- +bibtex_bibfiles = ["./references.bib"] + + +# using this method to link github source code as the above method was working with +# brain print project +# from urllib.parse import quote + + +# def linkcode_resolve(domain, info): +# if domain != "py": +# return None +# if not info["module"]: +# return None + +# filename = quote(info["module"].replace(".", "/")) +# if not filename.startswith("tests"): +# filename = "/" + filename + +# if "fullname" in info: +# anchor = info["fullname"] +# anchor = "#:~:text=" + quote(anchor.split(".")[-1]) +# else: +# anchor = "" + +# result = f"{gh_url}/blob/master/{filename}.py{anchor}" +# return result + +# -- sphinx.ext.linkcode ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/extensions/linkcode.html + + +def linkcode_resolve(domain: str, info: Dict[str, str]) -> Optional[str]: + """Determine the URL corresponding to a Python object. + + Parameters + ---------- + domain : str + One of 'py', 'c', 'cpp', 'javascript'. + info : dict + With keys "module" and "fullname". + + Returns + ------- + url : str | None + The code URL. If None, no link is added. + """ + if domain != "py": + return None # only document python objects + + # retrieve pyobject and file + try: + module = import_module(info["module"]) + pyobject = module + for elt in info["fullname"].split("."): + pyobject = getattr(pyobject, elt) + fname = inspect.getsourcefile(pyobject).replace("\\", "/") + except Exception: + # Either the object could not be loaded or the file was not found. + # For instance, properties will raise. + return None + + # retrieve start/stop lines + source, start_line = inspect.getsourcelines(pyobject) + lines = "L%d-L%d" % (start_line, start_line + len(source) - 1) + + # create URL + if "dev" in release: + branch = "main" + else: + return None # alternatively, link to a maint/version branch + fname = fname.rsplit(f"/{package}/")[1] + url = f"{gh_url}/blob/{branch}/{package}/{fname}#{lines}" + return url + + +import os + +# -- make sure pandoc gets installed ----------------------------------------- +from inspect import getsourcefile + +# Get path to directory containing this file, conf.py. +DOCS_DIRECTORY = os.path.dirname(os.path.abspath(getsourcefile(lambda: 0))) + + +def ensure_pandoc_installed(_): + import pypandoc + + # Download pandoc if necessary. If pandoc is already installed and on + # the PATH, the installed version will be used. Otherwise, we will + # download a copy of pandoc into docs/bin/ and add that to our PATH. + pandoc_dir = os.path.join(DOCS_DIRECTORY, "bin") + # Add dir containing pandoc binary to the PATH environment variable + if pandoc_dir not in os.environ["PATH"].split(os.pathsep): + os.environ["PATH"] += os.pathsep + pandoc_dir + pypandoc.ensure_pandoc_installed( + targetfolder=pandoc_dir, + delete_installer=True, + ) + + +def setup(app): + app.connect("builder-inited", ensure_pandoc_installed) diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..e317264 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,140 @@ +.. include:: ./links.inc + +.. .. literalinclude:: ../README.md +.. :language: markdown + +.. image:: https://badge.fury.io/py/brainprint.svg + :target: https://pypi.org/project/brainprint/ + +BrainPrint +========== + +This is the `brainprint` python package, a derivative of the original `BrainPrint-legacy `_ scripts, +with the primary goal to provide a Python-only version, to integrate the `LaPy `_ package, +and to remove dependencies on third-party software (shapeDNA-* binaries, gmsh, meshfix). +As a result, some functionality of the original BrainPrint-legacy scripts is no longer maintained (currently no support of tetrahedral meshes and no support of cortical parcellations or label files). + +Installation +------------ + +Use the following code to install the latest release of LaPy into your local Python package directory: + +.. code-block:: bash + + python3 -m pip install brainprint + +This will also install the necessary dependencies, e.g. the `LaPy `_ package. +You may need to add your local Python package directory to your $PATH in order to run the scripts. + +Usage +----- + +Command Line Interface (CLI) +---------------------------- + +Once installed, the package provides a `brainprint` executable which can be run from the command line. + +The `brainprint` CLI enables per-subject computation of the individual brainprint descriptors. Its usage and options are summarized below; detailed info is available by calling the script without any arguments from the command line. + +brainprint Command-Line Interface +''''''''''''''''''''''''''''''''' + +.. code-block:: sh + + brainprint + --sdir + --sid + [--num ] [--evec] [--skipcortex] + [--norm ] + [--reweight] [--asymmetry] [--outdir ] + [--help] [--more-help] + +Options +''''''' + +``--help`` Show this help message and exit. + +``--more-help`` Show extensive help message and exit. + +Required options +'''''''''''''''' + +``--sid `` Subject ID (FreeSurfer-processed directory inside the subjects directory). + +``--sdir `` FreeSurfer subjects directory. + +Processing directives +''''''''''''''''''''' + +``--num `` Number of eigenvalues/vectors to compute (default: 50). + +``--evec`` Switch on eigenvector computation (default: off). + +``--skipcortex`` Skip cortical surfaces (default: off). + +``--norm `` Switch on eigenvalue normalization; will be either surface, volume, or determined by the geometry of the object. Use "none" or leave out entirely to skip normalization. + +``--reweight`` Switch on eigenvalue reweighting (default: off). + +``--asymmetry`` Perform left-right asymmetry calculation (default: off). + +``--cholmod`` Switch on use of (faster) Cholesky decomposition instead of (slower) LU decomposition (default: off). May require manual install of scikit-sparse package. + +Output parameters +''''''''''''''''' + +``--outdir=OUTDIR`` Output directory (default: //brainprint). + +``--keep-temp`` Whether to keep the temporary files directory or not (default: False). + + +Python Package +-------------- + +`brainprint` can also be run within a pure Python environment, i.e. installed and imported as a Python package. E.g.: + +.. code-block:: python + + from brainprint import Brainprint + + subjects_dir = "/path/to/freesurfer/subjects_dir/" + subject_id = "42" + + bp = Brainprint(subjects_dir=subjects_dir, asymmetry=True, keep_eigenvectors=True) + results = bp.run(subject_id=subject_id) + results + {"eigenvalues": PosixPath("/path/to/freesurfer/subjects_dir/subject_id/brainprint/subject_id.brainprint.csv"), + "eigenvectors": PosixPath("/path/to/freesurfer/subjects_dir/subject_id/brainprint/eigenvectors"), + "distances": PosixPath("/path/to/freesurfer/subjects_dir/subject_id/brainprint/subject_id.brainprint.asymmetry.csv")} + + +Output +------ +The script will create an output directory that contains a CSV table with +values (in that order) for the area, volume, and first n eigenvalues per each +FreeSurfer structure. An additional output file will be created if the +asymmetry calculation is performed and/or for the eigenvectors (CLI `--evecs` flag or `keep_eigenvectors` on class initialization). + +Changes +------- + +There are some changes in functionality in comparison to the original `BrainPrint `_ scripts: + +- Currently, there is no support for tetrahedral meshes. +- Currently, there is no support for analyses of cortical parcellation or label files. +- No more Python 2.x compatibility. + +References +---------- + +If you use this software for a publication, please cite: + +[1] BrainPrint: a discriminative characterization of brain morphology. Wachinger C, Golland P, Kremen W, Fischl B, Reuter M. Neuroimage. 2015;109:232-48. `http://dx.doi.org/10.1016/j.neuroimage.2015.01.032 `_ `PubMed `_ + +[2] Laplace-Beltrami spectra as 'Shape-DNA' of surfaces and solids. Reuter M, Wolter F-E, Peinecke N. Computer-Aided Design. 2006;38:342-366. `http://dx.doi.org/10.1016/j.cad.2005.10.011 `_ + + +.. toctree:: + :hidden: + + api/index diff --git a/doc/links.inc b/doc/links.inc new file mode 100644 index 0000000..0794c60 --- /dev/null +++ b/doc/links.inc @@ -0,0 +1,36 @@ +.. This (-*- rst -*-) format file contains commonly used link targets and name + substitutions. It may be included in many files, therefore it should only + contain link targets and name substitutions. Try grepping for "^\.\. _" to + find plausible candidates for this list. + +.. NOTE: reST targets are + __not_case_sensitive__, so only one target definition is needed for: + nipy, NIPY, Nipy, etc... + + +.. project + +.. _project pypi: https://pypi.org/project/lapy/ +.. _project conda: https://anaconda.org/conda-forge/lapy +.. _project github: https://github.com/Deep-MI/BrainPrint +.. _project license: https://github.com/Deep-MI/BrainPrint/blob/main/LICENSE +.. _project brainprint: https://pypi.org/project/brainprint/ + +.. license + +.. _MIT license: https://opensource.org/licenses/MIT + + +.. numpy + +.. _numpy: https://numpy.org/ + + +.. sklearn + +.. _scikit-learn: https://scikit-learn.org/stable/ + + +.. scipy + +.. _scipy: https://scipy.org/ diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%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.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/doc/references.bib b/doc/references.bib new file mode 100644 index 0000000..df8e0f3 --- /dev/null +++ b/doc/references.bib @@ -0,0 +1,23 @@ +@article{numpy_2020, + author = {Harris, Charles R. and Millman, K. Jarrod and van der Walt, Stéfan J. and Gommers, Ralf and Virtanen, Pauli and Cournapeau, David and Wieser, Eric and Taylor, Julian and Berg, Sebastian and Smith, Nathaniel J. and Kern, Robert and Picus, Matti and Hoyer, Stephan and van Kerkwijk, Marten H. and Brett, Matthew and Haldane, Allan and del Río, Jaime Fernández and Wiebe, Mark and Peterson, Pearu and Gérard-Marchant, Pierre and Sheppard, Kevin and Reddy, Tyler and Weckesser, Warren and Abbasi, Hameer and Gohlke, Christoph and Oliphant, Travis E.}, + doi = {10.1038/s41586-020-2649-2}, + journal = {Nature}, + month = {September}, + number = {7825}, + pages = {357--362}, + title = {Array programming with {NumPy}}, + volume = {585}, + year = {2020} +} + +@article{scipy_2020, + author = {Virtanen, Pauli and Gommers, Ralf and Oliphant, Travis E. and Haberland, Matt and Reddy, Tyler and Cournapeau, David and Burovski, Evgeni and Peterson, Pearu and Weckesser, Warren and Bright, Jonathan and van der Walt, Stéfan J. and Brett, Matthew and Wilson, Joshua and Millman, K. Jarrod and Mayorov, Nikolay and Nelson, Andrew R. J. and Jones, Eric and Kern, Robert and Larson, Eric and Carey, C J and Polat, İlhan and Feng, Yu and Moore, Eric W. and VanderPlas, Jake and Laxalde, Denis and Perktold, Josef and Cimrman, Robert and Henriksen, Ian and Quintero, E. A. and Harris, Charles R. and Archibald, Anne M. and Ribeiro, Antônio H. and Pedregosa, Fabian and van Mulbregt, Paul and {SciPy 1.0 Contributors} and Vijaykumar, Aditya and Bardelli, Alessandro Pietro and Rothberg, Alex and Hilboll, Andreas and Kloeckner, Andreas and Scopatz, Anthony and Lee, Antony and Rokem, Ariel and Woods, C. Nathan and Fulton, Chad and Masson, Charles and Häggström, Christian and Fitzgerald, Clark and Nicholson, David A. and Hagen, David R. and Pasechnik, Dmitrii V. and Olivetti, Emanuele and Martin, Eric and Wieser, Eric and Silva, Fabrice and Lenders, Felix and Wilhelm, Florian and Young, G. and Price, Gavin A. and Ingold, Gert-Ludwig and Allen, Gregory E. and Lee, Gregory R. and Audren, Hervé and Probst, Irvin and Dietrich, Jörg P. and Silterra, Jacob and Webber, James T and Slavič, Janko and Nothman, Joel and Buchner, Johannes and Kulick, Johannes and Schönberger, Johannes L. and de Miranda Cardoso, José Vinícius and Reimer, Joscha and Harrington, Joseph and Rodríguez, Juan Luis Cano and Nunez-Iglesias, Juan and Kuczynski, Justin and Tritz, Kevin and Thoma, Martin and Newville, Matthew and Kümmerer, Matthias and Bolingbroke, Maximilian and Tartre, Michael and Pak, Mikhail and Smith, Nathaniel J. and Nowaczyk, Nikolai and Shebanov, Nikolay and Pavlyk, Oleksandr and Brodtkorb, Per A. and Lee, Perry and McGibbon, Robert T. and Feldbauer, Roman and Lewis, Sam and Tygier, Sam and Sievert, Scott and Vigna, Sebastiano and Peterson, Stefan and More, Surhud and Pudlik, Tadeusz and Oshima, Takuya and Pingel, Thomas J. and Robitaille, Thomas P. and Spura, Thomas and Jones, Thouis R. and Cera, Tim and Leslie, Tim and Zito, Tiziano and Krauss, Tom and Upadhyay, Utkarsh and Halchenko, Yaroslav O. and Vázquez-Baeza, Yoshiki}, + doi = {10.1038/s41592-019-0686-2}, + journal = {Nature Methods}, + month = {March}, + number = {3}, + pages = {261--272}, + title = {{SciPy} 1.0: fundamental algorithms for scientific computing in {Python}}, + volume = {17}, + year = {2020} +}