+
[](https://github.com/yxlao/camtools/actions/workflows/formatter.yml)
[](https://github.com/yxlao/camtools/actions/workflows/unit_test.yml)
[](https://github.com/yxlao/camtools/actions/workflows/pypi.yml)
-[](https://github.com/yxlao/camtools)
-[](https://gitee.com/yxlao/camtools)
[](https://pypi.org/project/camtools)
+[](https://camtools.readthedocs.io/en/latest/?badge=latest)
CamTools is a collection of tools for handling cameras in computer vision. It
can be used for plotting, converting, projecting, ray casting, and doing more
@@ -20,8 +26,13 @@ clear and easy-to-use APIs.
-
-
+
+
@@ -251,6 +262,31 @@ the beginning of the README.
[part 2](https://ksimek.github.io/2012/08/22/extrinsic/),
and [part 3](https://ksimek.github.io/2013/08/13/intrinsic/).
+## Building Documentation
+
+To build and view the documentation locally:
+
+```bash
+# Install documentation dependencies
+pip install -e .[docs]
+
+# Build the documentation
+make -C docs clean && make -C docs html
+
+# (Optional) Build the documentation with warnings as errors
+make -C docs clean && make -C docs html SPHINXOPTS="-W --keep-going"
+
+# Start a local server to view the documentation
+python -m http.server 8000 --directory docs/_build/html
+```
+
+Then open your browser and navigate to `http://localhost:8000` to view the documentation.
+
+The documentation is also automatically built by GitHub Actions on pull requests and pushes to main. After merging to main, you can view:
+
+- Public documentation at https://camtools.readthedocs.io/en/latest/
+- Admin panel at https://app.readthedocs.org/projects/camtools/
+
## Contributing
- Follow [Angular's commit message convention](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format) for PRs.
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..d4bb2cbb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100644
index 00000000..b1cf0bdc
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,26 @@
+API Reference
+============
+
+.. toctree::
+ :maxdepth: 1
+
+ api/camera
+ api/colormap
+ api/convert
+ api/geometry
+ api/image
+ api/io
+ api/metric
+ api/normalize
+ api/project
+ api/raycast
+ api/render
+ api/solver
+ api/transform
+
+
+.. Not included in the docs website:
+.. api/artifact
+.. api/colmap
+.. api/sanity
+.. api/util
diff --git a/docs/api/camera.rst b/docs/api/camera.rst
new file mode 100644
index 00000000..e0d109ce
--- /dev/null
+++ b/docs/api/camera.rst
@@ -0,0 +1,9 @@
+ct.camera
+=========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.camera
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/colormap.rst b/docs/api/colormap.rst
new file mode 100644
index 00000000..ae2257e9
--- /dev/null
+++ b/docs/api/colormap.rst
@@ -0,0 +1,9 @@
+ct.colormap
+===========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.colormap
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/convert.rst b/docs/api/convert.rst
new file mode 100644
index 00000000..0959fc98
--- /dev/null
+++ b/docs/api/convert.rst
@@ -0,0 +1,9 @@
+ct.convert
+==========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.convert
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/geometry.rst b/docs/api/geometry.rst
new file mode 100644
index 00000000..88b6d8aa
--- /dev/null
+++ b/docs/api/geometry.rst
@@ -0,0 +1,9 @@
+ct.geometry
+===========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.geometry
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/image.rst b/docs/api/image.rst
new file mode 100644
index 00000000..3d4344b1
--- /dev/null
+++ b/docs/api/image.rst
@@ -0,0 +1,9 @@
+ct.image
+========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.image
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/io.rst b/docs/api/io.rst
new file mode 100644
index 00000000..c0e03d96
--- /dev/null
+++ b/docs/api/io.rst
@@ -0,0 +1,9 @@
+ct.io
+=======
+
+.. currentmodule:: ct
+
+.. automodule:: ct.io
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/metric.rst b/docs/api/metric.rst
new file mode 100644
index 00000000..69b32400
--- /dev/null
+++ b/docs/api/metric.rst
@@ -0,0 +1,9 @@
+ct.metric
+=========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.metric
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/normalize.rst b/docs/api/normalize.rst
new file mode 100644
index 00000000..94df2fb1
--- /dev/null
+++ b/docs/api/normalize.rst
@@ -0,0 +1,9 @@
+ct.normalize
+===========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.normalize
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/project.rst b/docs/api/project.rst
new file mode 100644
index 00000000..7724b783
--- /dev/null
+++ b/docs/api/project.rst
@@ -0,0 +1,9 @@
+ct.project
+==========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.project
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/raycast.rst b/docs/api/raycast.rst
new file mode 100644
index 00000000..90ae6365
--- /dev/null
+++ b/docs/api/raycast.rst
@@ -0,0 +1,9 @@
+ct.raycast
+==========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.raycast
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/render.rst b/docs/api/render.rst
new file mode 100644
index 00000000..d715ad5a
--- /dev/null
+++ b/docs/api/render.rst
@@ -0,0 +1,9 @@
+ct.render
+=========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.render
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/solver.rst b/docs/api/solver.rst
new file mode 100644
index 00000000..a2b87679
--- /dev/null
+++ b/docs/api/solver.rst
@@ -0,0 +1,9 @@
+ct.solver
+=========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.solver
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/transform.rst b/docs/api/transform.rst
new file mode 100644
index 00000000..89fca24e
--- /dev/null
+++ b/docs/api/transform.rst
@@ -0,0 +1,9 @@
+ct.transform
+===========
+
+.. currentmodule:: ct
+
+.. automodule:: ct.transform
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/camera.rst b/docs/camera.rst
new file mode 100644
index 00000000..5676b3a0
--- /dev/null
+++ b/docs/camera.rst
@@ -0,0 +1,173 @@
+Camera Coordinates
+==================
+
+.. only:: not latex
+
+ .. image:: https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camera_coordinates_light.png
+ :width: 520
+ :align: center
+ :alt: Camera Coordinates
+ :class: only-light
+
+ .. image:: https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camera_coordinates_dark.png
+ :width: 520
+ :align: center
+ :alt: Camera Coordinates
+ :class: only-dark
+
+A homogeneous point ``[X, Y, Z, 1]`` in the world coordinate can be projected to a
+homogeneous point ``[x, y, 1]`` in the image (pixel) coordinate using the
+following equation:
+
+.. math::
+
+ \lambda
+ \left[\begin{array}{l}
+ x \\
+ y \\
+ 1
+ \end{array}\right]=\left[\begin{array}{ccc}
+ f_{x} & 0 & c_{x} \\
+ 0 & f_{y} & c_{y} \\
+ 0 & 0 & 1
+ \end{array}\right]\left[\begin{array}{llll}
+ R_{00} & R_{01} & R_{02} & t_{0} \\
+ R_{10} & R_{11} & R_{12} & t_{1} \\
+ R_{20} & R_{21} & R_{22} & t_{2}
+ \end{array}\right]\left[\begin{array}{c}
+ X \\
+ Y \\
+ Z \\
+ 1
+ \end{array}\right].
+
+We follow the standard OpenCV-style camera coordinate system as illustrated at
+the beginning of the documentation.
+
+Camera Coordinate
+-----------------
+
+Right-handed, with :math:`Z` pointing away from the camera towards the view direction
+and :math:`Y` axis pointing down. Note that the OpenCV convention (camtools' default)
+is different from the OpenGL/Blender convention, where :math:`Z` points towards the
+opposite view direction, :math:`Y` points up and :math:`X` points right.
+
+To convert between the OpenCV camera coordinates and the OpenGL-style coordinates,
+use the conversion functions:
+
+- ``ct.convert.T_opencv_to_opengl()``
+- ``ct.convert.T_opengl_to_opencv()``
+- ``ct.convert.pose_opencv_to_opengl()``
+- ``ct.convert.pose_opengl_to_opencv()``
+
+Image Coordinate
+----------------
+
+Starts from the top-left corner of the image, with :math:`x` pointing right
+(corresponding to the image width) and :math:`y` pointing down (corresponding to
+the image height). This is consistent with OpenCV.
+
+Pay attention that the 0th dimension in the image array is the height (i.e., :math:`y`)
+and the 1st dimension is the width (i.e., :math:`x`). That is:
+
+- :math:`x` <=> ``u`` <=> width <=> column <=> the 1st dimension
+- :math:`y` <=> ``v`` <=> height <=> row <=> the 0th dimension
+
+Matrix Definitions
+------------------
+
+Camera Intrinsic Matrix (K)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+``K`` is a ``(3, 3)`` camera intrinsic matrix:
+
+.. code-block:: python
+
+ K = [[fx, s, cx],
+ [ 0, fy, cy],
+ [ 0, 0, 1]]
+
+Camera Extrinsic Matrix (T or W2C)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+``T`` is a ``(4, 4)`` camera extrinsic matrix:
+
+.. code-block:: python
+
+ T = [[R | t = [[R00, R01, R02, t0],
+ 0 | 1]] [R10, R11, R12, t1],
+ [R20, R21, R22, t2],
+ [ 0, 0, 0, 1]]
+
+- ``T`` is also known as the world-to-camera ``W2C`` matrix, which transforms a
+ point in the world coordinate to the camera coordinate.
+- ``T``'s shape is ``(4, 4)``, not ``(3, 4)``.
+- ``T`` is the inverse of ``pose``, i.e., ``np.linalg.inv(T) == pose``.
+- The camera center ``C`` in world coordinate is projected to ``[0, 0, 0, 1]`` in
+ camera coordinate.
+
+Rotation Matrix (R)
+^^^^^^^^^^^^^^^^^^^
+
+``R`` is a ``(3, 3)`` rotation matrix:
+
+.. code-block:: python
+
+ R = T[:3, :3]
+
+- ``R`` is a rotation matrix. It is an orthogonal matrix with determinant 1, as
+ rotations preserve volume and orientation.
+ - ``R.T == np.linalg.inv(R)``
+ - ``np.linalg.norm(R @ x) == np.linalg.norm(x)``, where ``x`` is a ``(3,)`` vector.
+
+Translation Vector (t)
+^^^^^^^^^^^^^^^^^^^^^^
+
+``t`` is a ``(3,)`` translation vector:
+
+.. code-block:: python
+
+ t = T[:3, 3]
+
+- ``t``'s shape is ``(3,)``, not ``(3, 1)``.
+
+Camera Pose Matrix (pose or C2W)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+``pose`` is a ``(4, 4)`` camera pose matrix. It is the inverse of ``T``.
+
+- ``pose`` is also known as the camera-to-world ``C2W`` matrix, which transforms a
+ point in the camera coordinate to the world coordinate.
+- ``pose`` is the inverse of ``T``, i.e., ``pose == np.linalg.inv(T)``.
+
+Camera Center (C)
+^^^^^^^^^^^^^^^^^
+
+``C`` is the camera center:
+
+.. code-block:: python
+
+ C = pose[:3, 3]
+
+- ``C``'s shape is ``(3,)``, not ``(3, 1)``.
+- ``C`` is the camera center in world coordinate. It is also the translation
+ vector of ``pose``.
+
+Projection Matrix (P)
+^^^^^^^^^^^^^^^^^^^^^
+
+``P`` is a ``(3, 4)`` camera projection matrix:
+
+- ``P`` is the world-to-pixel projection matrix, which projects a point in the
+ homogeneous world coordinate to the homogeneous pixel coordinate.
+- ``P`` is the product of the intrinsic and extrinsic parameters:
+
+ .. code-block:: python
+
+ # P = K @ [R | t]
+ P = K @ np.hstack([R, t[:, None]])
+
+- ``P``'s shape is ``(3, 4)``, not ``(4, 4)``.
+- It is possible to decompose ``P`` into intrinsic and extrinsic matrices by QR
+ decomposition.
+- Don't confuse ``P`` with ``pose``. Don't confuse ``P`` with ``T``.
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 00000000..ee7be984
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,103 @@
+# 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 os
+import sys
+import subprocess
+import camtools as ct
+
+sys.path.insert(0, os.path.abspath(".."))
+
+# Get version from camtools package
+version = ct.__version__
+
+# Get git commit hash
+try:
+ git_hash = (
+ subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
+ .decode("ascii")
+ .strip()
+ )
+ release = f"{version}+{git_hash}"
+except subprocess.CalledProcessError:
+ release = version
+
+project = "CamTools"
+copyright = "2024, Yixing Lao"
+author = "Yixing Lao"
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = [
+ "sphinx.ext.autodoc",
+ "sphinx.ext.viewcode",
+ "sphinx.ext.napoleon",
+ "sphinx.ext.intersphinx",
+ "myst_parser",
+]
+
+templates_path = ["_templates"]
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+language = "en"
+
+# -- 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"]
+
+# Furo theme options
+html_theme_options = {
+ "light_css_variables": {
+ "color-brand-primary": "#2962ff",
+ "color-brand-content": "#2962ff",
+ },
+ "dark_css_variables": {
+ "color-brand-primary": "#5c85ff",
+ "color-brand-content": "#5c85ff",
+ },
+}
+
+# Intersphinx configuration
+intersphinx_mapping = {
+ "python": ("https://docs.python.org/3", None),
+ "numpy": ("https://numpy.org/doc/stable/", None),
+}
+
+# Napoleon settings
+napoleon_google_docstring = True
+napoleon_numpy_docstring = True
+napoleon_include_init_with_doc = True
+napoleon_include_private_with_doc = False
+napoleon_include_special_with_doc = True
+napoleon_use_admonition_for_examples = False
+napoleon_use_admonition_for_notes = False
+napoleon_use_admonition_for_references = False
+napoleon_use_ivar = False
+napoleon_use_param = True
+napoleon_use_rtype = True
+napoleon_type_aliases = None
+
+# AutoDoc settings
+autodoc_member_order = "bysource"
+autodoc_typehints = "description"
+autodoc_typehints_description_target = "documented"
+add_module_names = True
+python_use_unqualified_type_names = False
+
+# Custom module name display
+modindex_common_prefix = ["camtools."] # Strip 'camtools.' from module index
+
+sys.modules["ct"] = ct # Allow using 'ct' as an alias in documentation
+
+# Suppress specific warnings
+suppress_warnings = [
+ "toctree.excluded", # Suppress warnings about files not in any toctree
+]
diff --git a/docs/contributing.rst b/docs/contributing.rst
new file mode 100644
index 00000000..28a3dfa1
--- /dev/null
+++ b/docs/contributing.rst
@@ -0,0 +1,40 @@
+Contributing
+============
+
+Contributing Guidelines
+-----------------------
+
+- Follow `Angular's commit message convention `_ for PRs.
+ - This applies to PR's title and ultimately the commit messages in ``main``.
+ - The prefix shall be one of ``build``, ``ci``, ``docs``, ``feat``, ``fix``, ``perf``, ``refactor``, ``test``.
+ - Use lowercase.
+- Format your code with `black `_. This will be enforced by the CI.
+
+Building Documentation
+----------------------
+
+To build and view the documentation locally:
+
+.. code-block:: bash
+
+ # Build the documentation
+ cd docs
+ make html
+
+ # Start a local server to view the documentation
+ python -m http.server 8000 --directory _build/html
+
+Then open your browser and navigate to ``http://localhost:8000`` to view the documentation.
+
+Build with CamTools
+-------------------
+
+If you use CamTools in your project, consider adding one of the following
+badges to your project.
+
+.. raw:: html
+
+
+
+
+
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 00000000..5d199762
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,150 @@
+CamTools Documentation
+======================
+
+.. only:: not latex
+
+ .. image:: https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camtools_logo_light.png
+ :width: 360
+ :align: center
+ :alt: CamTools Logono
+ :class: only-light
+
+ .. image:: https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camtools_logo_dark.png
+ :width: 360
+ :align: center
+ :alt: CamTools Logo
+ :class: only-dark
+
+.. raw:: html
+
+
+
+CamTools is a collection of tools for handling cameras in computer vision. It
+can be used for plotting, converting, projecting, ray casting, and doing more
+with camera parameters. It follows the standard camera coordinate system with
+clear and easy-to-use APIs.
+
+.. only:: not latex
+
+ .. image:: https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camera_coordinates_light.png
+ :width: 520
+ :align: center
+ :alt: Camera Coordinates
+ :class: only-light
+
+ .. image:: https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camera_coordinates_dark.png
+ :width: 520
+ :align: center
+ :alt: Camera Coordinates
+ :class: only-dark
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Docs
+
+ Home
+ camera
+ installation
+ contributing
+ api
+
+
+What can you do with CamTools?
+------------------------------
+
+1. Plot cameras
+^^^^^^^^^^^^^^^
+
+Useful for debugging 3D reconstruction and NeRFs!
+
+.. code-block:: python
+
+ import camtools as ct
+ import open3d as o3d
+ cameras = ct.camera.create_camera_frustums(Ks, Ts)
+ o3d.visualization.draw_geometries([cameras])
+
+.. image:: https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camera_frames.png
+ :width: 360
+ :align: center
+ :alt: Camera Frames
+
+2. Convert camera parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+
+ pose = ct.convert.T_to_pose(T) # Convert T to pose
+ T = ct.convert.pose_to_T(pose) # Convert pose to T
+ R, t = ct.convert.T_to_R_t(T) # Convert T to R and t
+ C = ct.convert.pose_to_C(pose) # Convert pose to camera center
+ K, T = ct.convert.P_to_K_T(P) # Decompose projection matrix P to K and T
+ # And more...
+
+3. Projection and ray casting
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+
+ # Project 3D points to pixels.
+ pixels = ct.project.points_to_pixel(points, K, T)
+
+ # Back-project depth image to 3D points.
+ points = ct.project.im_depth_to_points(im_depth, K, T)
+
+ # Ray cast a triangle mesh to depth image given the camera parameters.
+ im_depth = ct.raycast.mesh_to_im_depth(mesh, K, T, height, width)
+
+ # And more...
+
+4. Image and depth I/O
+^^^^^^^^^^^^^^^^^^^^^^
+
+Strict type checks and range checks are enforced. The image and depth I/O
+APIs are specifically designed to solve the following pain points:
+
+- Is my image of type ``float32`` or ``uint8``?
+- Does it have range ``[0, 1]`` or ``[0, 255]``?
+- Is it RGB or BGR?
+- Does my image have an alpha channel?
+- When saving depth image as integer-based ``.png``, is it correctly scaled?
+
+.. code-block:: python
+
+ ct.io.imread()
+ ct.io.imwrite()
+ ct.io.imread_detph()
+ ct.io.imwrite_depth()
+
+5. Command-line tools
+^^^^^^^^^^^^^^^^^^^^^
+
+The ``ct`` command runs in terminal:
+
+.. code-block:: bash
+
+ # Crop image boarders.
+ ct crop-boarders *.png --pad_pixel 10 --skip_cropped --same_crop
+
+ # Draw synchronized bounding boxes interactively.
+ ct draw-bboxes path/to/a.png path/to/b.png
+
+ # For more command-line tools.
+ ct --help
+
+.. raw:: html
+
+
+
+
+
+6. And more
+^^^^^^^^^^^
+
+- Solve line intersections
+- COLMAP tools
+- Points normalization
+- And more...
diff --git a/docs/installation.rst b/docs/installation.rst
new file mode 100644
index 00000000..1695a722
--- /dev/null
+++ b/docs/installation.rst
@@ -0,0 +1,41 @@
+Installation
+============
+
+Quick Installation
+------------------
+
+To install CamTools, simply do:
+
+.. code-block:: bash
+
+ pip install camtools
+
+Installation from Source
+------------------------
+
+Alternatively, you can install CamTools from source with one of the following
+methods:
+
+.. code-block:: bash
+
+ git clone https://github.com/yxlao/camtools.git
+ cd camtools
+
+ # Installation mode, if you want to use camtools only.
+ pip install .
+
+ # Editable mode, if you want to modify camtools on the fly.
+ pip install -e .
+
+ # Editable mode and dev dependencies.
+ pip install -e .[dev]
+
+ # Help VSCode resolve imports when installed with editable mode.
+ # https://stackoverflow.com/a/76897706/1255535
+ pip install -e .[dev] --config-settings editable_mode=strict
+
+ # Enable torch-related features (e.g. computing image metrics)
+ pip install camtools[torch]
+
+ # Enable torch-related features in editable mode
+ pip install -e .[torch]
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 00000000..32bb2452
--- /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=.
+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/pyproject.toml b/pyproject.toml
index 124e3ec5..67401617 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -38,6 +38,13 @@ dev = [
"pytest-benchmark>=4.0.0",
"ipdb",
]
+docs = [
+ "sphinx",
+ "sphinx-rtd-theme",
+ "myst-parser",
+ "furo",
+ "tomli",
+]
torch = [
"torch>=1.8.0",
"lpips>=0.1.4",