CCD Data reduction (`ccdproc`)
==============================

Introduction
------------

.. note::
    `ccdproc` works only with astropy version 0.4.0 or later.

The `ccdproc` package provides:

+ An image class, `~ccdproc.CCDData`, that includes an uncertainty for the 
  data, units and methods for performing arithmetic with images including the
  propagation of uncertainties.
+ A set of functions performing common CCD data reduction steps (e.g. dark
  subtraction, flat field correction) with a flexible mechanism for logging
  reduction steps in the image metadata.
+ A class for combining and/or clipping images, `~ccdproc.Combiner`, and
  associated functions.

Getting Started
---------------

A ``CCDData`` object can be created from a numpy array (masked or not) or from
a FITS file:

    >>> import numpy as np
    >>> from astropy import units as u
    >>> import ccdproc
    >>> image_1 = ccdproc.CCDData(np.ones((10, 10)), unit="adu")

An example of reading from a FITS file is
``image_2 = ccdproc.CCDData.read('my_image.fits', unit="electron")`` (the 
``electron`` unit is defined as part of ``ccdproc``).

The metadata of a ``CCDData`` object may be any dictionary-like object, including a FITS header. When a ``CCDData`` object is initialized from FITS file its metadata is a FITS header.

The data is accessible either by indexing directly or through the ``data``
attribute:

    >>> sub_image = image_1[:, 1:-3]  # a CCDData object
    >>> sub_data =  image_1.data[:, 1:-3]  # a numpy array

See the documentation for `~ccdproc.CCDData` for a complete list of attributes.

Most operations are performed by functions in `ccdproc`:

    >>> dark = ccdproc.CCDData(np.random.normal(size=(10, 10)), unit="adu")
    >>> dark_sub = ccdproc.subtract_dark(image_1, dark,
    ...                                  dark_exposure=30*u.second,
    ...                                  data_exposure=15*u.second,
    ...                                  scale=True)

Every function returns a *copy* of the data with the operation performed. If,
for some reason, you wanted to modify the data in-place, do this:

    >>> image_2 = ccdproc.subtract_dark(image_1, dark, dark_exposure=30*u.second, data_exposure=15*u.second, scale=True)

See the documentation for `~ccdproc.subtract_dark` for more compact
ways of providing exposure times.

Every function in `ccdproc` supports logging through the addition of
information to the image metadata.

Logging can be simple -- add a string to the metadata:

    >>> image_2_gained = ccdproc.gain_correct(image_2, 1.5 * u.photon/u.adu, add_keyword='gain_corrected')

Logging can be more complicated -- add several keyword/value pairs by passing
a dictionary to ``add_keyword``:

    >>> my_log = {'gain_correct': 'Gain value was 1.5',
    ...           'calstat': 'G'}
    >>> image_2_gained = ccdproc.gain_correct(image_2,
    ...                                       1.5 * u.photon/u.adu,
    ...                                       add_keyword=my_log)

The `~ccdproc.ccdproc.Keyword` class provides a compromise between the simple
and complicated cases for providing a single key/value pair:

    >>> key = ccdproc.Keyword('gain_corrected', value='Yes')
    >>> image_2_gained = ccdproc.gain_correct(image_2,
    ...                                       1.5 * u.photon/u.adu,
    ...                                       add_keyword=key)

`~ccdproc.ccdproc.Keyword` also provides a convenient way to get a value from
image metadata and specify its unit:

    >>> image_2.header['gain']  = 1.5
    >>> gain = ccdproc.Keyword('gain', unit=u.photon/u.adu)
    >>> image_2_var = ccdproc.create_deviation(image_2,
    ...                                       gain=gain.value_from(image_2.header),
    ...                                       readnoise=3.0 * u.photon)

You might wonder why there is a `~ccdproc.gain_correct` at all, since the implemented
gain correction simple multiplies by a constant. There are two things you get
with `~ccdproc.gain_correct` that you do not get with multiplication:

+ Appropriate scaling of uncertainties.
+ Units

The same advantages apply to operations that are more complex, like flat
correction, in which one image is divided by another:

    >>> flat = ccdproc.CCDData(np.random.normal(1.0, scale=0.1, size=(10, 10)),
    ...                        unit='adu')
    >>> image_1_flat = ccdproc.flat_correct(image_1, flat)

In addition to doing the necessary division, `~ccdproc.flat_correct` propagates
uncertainties (if they are set).


Using `ccdproc`
---------------

.. toctree::
    :maxdepth: 1

    ccddata.rst
    image_combination.rst
    reduction_toolbox.rst
    reduction_examples.rst

.. automodapi:: ccdproc

.. _GitHub repo: https://github.com/astropy/ccdproc
