Switch to side-by-side view

--- a
+++ b/docs/source/guide_basic.rst
@@ -0,0 +1,167 @@
+.. _basic_usage:
+
+.. py:currentmodule:: dosma
+
+**This guide is still under construction**
+
+Basic Usage
+-----------
+
+Dosma is designed for simple imaging I/O, registration, quantitative fitting, and AI-based image processing. 
+
+Dosma does not bundle Tensorflow and Keras installation by default.
+To enable  support, you must install these libraries as an additional step.
+
+We use the following abbreviations for libraries:
+
+>>> import numpy as np
+>>> import dosma as dm
+
+
+Image I/O
+=========================
+
+Dosma provides data readers and writers to allow you to read/write image data stored in NIfTI and DICOM standards.
+These I/O tools create or write from the Dosma image class :class:`MedicalVolume`.
+
+For example to load a DICOM image series, which has multiple echos, with each echo corresponding to a volume,
+we can do:
+
+>>> dr = dm.DicomReader(num_workers=1, verbose=True) 
+>>> dr.load("/path/to/dicom/folder", group_by="EchoNumbers")
+
+We can also load specific files in the image series:
+
+>>> dr.load(["file1", "file2", ...], group_by="EchoNumbers")
+
+DICOM image data often has associated metadata. :class:`MedicalVolume` makes it easy to get
+and set metadata:
+
+>>> volume = volumes[0]  # first echo time
+>>> volume.get_metadata("EchoTime", float)
+10.0
+>>> volume.set_metadata("EchoTime", 20)
+>>> volume.get_metadata("EchoTime", float)
+20.0
+
+Similarly, to load a NIfTI volume, we use the :class:`NiftiReader` class:
+
+>>> nr = dm.NiftiReader()
+>>> volume = nr.load("/path/to/nifti/file.nii.gz")
+
+NIfTI volumes can also be loaded in memmap mode. This makes loading much faster and allows easy interaction
+with larger-than-memory arrays. Only when the volume is modified will the volume
+be loaded into memory and modified.
+
+>>> volume = nr.load("/path/to/nifti/file", mmap=True)
+
+Images in all supported data formats can also be loaded and written using ``dosma.read`` and ``dosma.write``:
+
+>>> import dosma as dm
+>>> dm.load("/path/to/dicom/folder", group_by="EchoNumbers")
+>>> dm.load("/path/to/nifti/file.nii.gz", mmap=True)
+
+
+Reformatting Images
+=========================
+
+Given the multiple different orientation conventions used by different image formats and libraries,
+reformatting medical images can be difficult to keep track of. Dosma simplifies this by introducing
+an unambiguous convention for image orientation based on the RAS+ coordinate system, in which all
+directions point to the increasing direction.
+
+To reformat a :class:`MedicalVolume` instance (``mv``) such that the dimensions correspond to
+superior -> inferior, anterior -> posterior, left -> right, we can do:
+
+>>> mv = mv.reformat(("SI", "AP", "LR"))
+
+To perform the operation in-place (i.e. modifying the existing instance), we can do:
+
+>>> mv = mv.reformat(("SI", "AP", "LR"), inplace=True)
+
+Note, in-place reformatting returns the same :class:`MedicalVolume` object that was modified
+in-place (i.e. ``self``) to allow chaining methods together.
+
+We may also want to reformat images to be in the same orientation as other images:
+
+>>> mv = mv.reformat_as(other_image)
+
+
+Image Slicing and Arithmetic Operations
+========================================
+
+:class:`MedicalVolume` supports some array-like functionality, including Python arithmetic
+operations (``+``, ``-``, ``**``, ``/``, ``//``), NumPy shape-preserving operations
+(e.g. ``np.exp``, ``np.log``, ``np.pow``, etc.), and slicing.
+
+>>> mv += 5
+>>> mv = mv * mv / mv
+>>> mv = np.exp(mv)
+>>> mv = mv[:5, :6, :7]
+
+Note, in order to preserve dimensions, slicing cannot be used to reduce dimensions.
+For example, the first line will throw an error; the second will not:
+
+>>> mv = mv[2]
+IndexError: Scalar indices disallowed in spatial dimensions; Use `[x]` or `x:x+1`
+>>> mv[2:3]
+
+
+NumPy Interoperability
+========================================
+
+In addition to standard shape-preserving universal functions (ufuncs) described above,
+:class:`MedicalVolume` also support a subset of other numpy functions that, like the ufuncs,
+operate on the pixel data in the medical volume:
+
+- Boolean Functions: :func:`numpy.all`, :func:`numpy.any`, :func:`numpy.where`
+- Statistics functions: :func:`numpy.mean`, :func:`numpy.sum`, :func:`numpy.std`, :func:`numpy.amin`, :func:`numpy.amax`, :func:`numpy.argmax`, :func:`numpy.argmin`
+- Rounding functions: :func:`numpy.round`, :func:`numpy.around`, :func:`numpy.round_`
+- NaN functions: :func:`numpy.nanmean`, :func:`numpy.nansum`, :func:`numpy.nanstd`, :func:`numpy.nan_to_num`
+
+For example, ``np.all(mv)`` is equivalent to ``np.all(mv.volume)``. Note, headers are not deep copied.
+NumPy operations that reduce spatial dimensions are not supported. For example, a 3D volume ``mv`` cannot
+be summed over any two of the first three axes:
+
+>>> np.sum(mv, 0)  # this will raise an error
+>>> np.sum(mv)  # this will return a scalar
+
+
+(BETA) Choosing A Computing Device
+========================================
+
+Dosma provides a device class :class:`dosma.Device`, which allows you to specify which device
+to use for :class:`MedicalVolume` operations. It extends the Device class from `CuPy <https://cupy.dev/>`_.
+To enable GPU computing support, install the correct build for CuPy on your machine.
+
+To move a MedicalVolume to GPU 0, you can use the :meth:`MedicalVolume.to` method:
+
+>>> mv_gpu = mv.to(dm.Device(0))
+
+You can also move the image back to the cpu:
+
+>>> mv_cpu = mv_gpu.cpu()  # or mv_gpu.to(dm.Device(-1))
+
+If the device is already on the specified device, the same object is returned.
+Note, some functionality such as curve fitting (:class:`dosma.curve_fit`), image registration,
+and image I/O are not supported with images on the GPU.
+
+
+(ALPHA) Multi-Library Interoperability
+========================================
+
+:class:`MedicalVolume` is also interoperable with popular image data structures
+with zero-copy, meaning array data will not be copied. Structures currently include the
+SimpleITK Image, Nibabel Nifti1Image, and PyTorch tensors.
+
+For example, we can use the :meth:`MedicalVolume.to_sitk` method to convert a MedicalVolume
+to a SimpleITK image:
+
+>>> sitk_img = mv.to_sitk()
+
+For PyTorch tensors, the zero-copy also applies to tensors on the GPU. Using ``mv_gpu``,
+which is on GPU 0, from the previous section, we can do:
+
+>>> torch_tensor = mv_gpu.to_torch()
+>>> torch.device
+cuda:0