IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Mar 15, 2005, 1:13:16 PM (21 years ago)
Author:
Paul Price
Message:

Added section on image combination and subtraction.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/doc/modules/ModulesSDRS.tex

    r3300 r3425  
    1 %%% $Id: ModulesSDRS.tex,v 1.32 2005-02-22 19:30:45 eugene Exp $
     1%%% $Id: ModulesSDRS.tex,v 1.33 2005-03-15 23:13:16 price Exp $
    22\documentclass[panstarrs]{panstarrs}
    33
     
    16151615\end{verbatim}
    16161616
     1617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     1618
     1619\section{Image Combination}
     1620
     1621The image combination for \PS{} will employ an iterative approach, in
     1622order to identify cosmic rays.  The first pass involves transforming
     1623and combining the input images, and noting pixels which are apparently
     1624deviant.  These pixels are examined in further detail, before a subset
     1625of them are declared to be bad, whereupon these pixels are
     1626re-transformed, and the images are combined properly.  Here we
     1627introduce two functions which will perform the combination and
     1628examination steps.  Prototype code exists for each of these functions.
     1629\tbd{For further details, see the document about image combination for
     1630\PS{}.}
     1631
     1632\subsection{Combining images}
     1633
     1634\begin{verbatim}
     1635psImage *pmCombineImages(psImage *combined, // Combined image
     1636                         psArray **questionablePixels, // Array of rejection masks
     1637                         const psArray *images, // Array of input images
     1638                         const psArray *errors, // Array of input error images
     1639                         const psArray *masks,// Array of input masks
     1640                         unsigned int maskVal, // Mask value
     1641                         const psArray *pixels, // Pixels to combine
     1642                         int numIter,   // Number of rejection iterations
     1643                         float sigmaClip, // Number of standard deviations at which to reject
     1644                         const psStats *stats // Statistics to use in the combination
     1645                         );
     1646\end{verbatim}
     1647
     1648\code{pmCombineImages} shall combine the input \code{images},
     1649returning the \code{combined} image and a list of
     1650\code{questionablePixels} in each input image.  The array of error
     1651images, \code{errors}, shall be used to calculate the value in the
     1652combined image and the list of questionable pixels, if
     1653non-\code{NULL}.  Pixels whose corresponding value in the array of
     1654mask images, \code{masks}, matches \code{maskVal} shall be masked from
     1655the combination.  The \code{images}, \code{errors} and \code{masks}
     1656arrays, if non-\code{NULL}, shall all carry the same number of images;
     1657otherwise the function shall generate an error and return \code{NULL}.
     1658The sizes of all images in the \code{images}, \code{errors} and
     1659\code{masks} arrays shall be identical; otherwise the function shall
     1660generate an error and return \code{NULL}.
     1661
     1662If \code{pixels} is non-\code{NULL}, only those pixels specified shall
     1663be combined.  The combination consists of \code{numIter} iterations in
     1664which a stack of pixels is combined using the specified \code{stats}.
     1665In each iteration, questionable pixels are identified as lying more
     1666than \code{sigmaClip} standard deviations from the combined value;
     1667these pixels are excluded from the stack for the next iteration.  The
     1668value for the combined image is that produced by the \textit{first}
     1669iteration (i.e., with no pixels excluded except those which have their
     1670corresponding mask match the \code{maskVal}); this allows subsequent
     1671calls to the function to only act on a small fraction of the pixels,
     1672since questionable pixels identified in the first call of the function
     1673will be properly rejected at a later point (see the example, below).
     1674
     1675In the event that \code{images} or \code{stats} are \code{NULL}, the
     1676function shall generate an error and return \code{NULL}.
     1677
     1678\subsection{Rejecting pixels}
     1679
     1680\begin{verbatim}
     1681psArray *pmRejectPixels(const psArray *images, // Array of input images
     1682                        const psArray *pixels, // These are the pixels which were rejected in the combination
     1683                        const psArray *inToOut, // Transformations from input to output system
     1684                        const psArray *outToIn, // Transformations from output to input system
     1685                        float rejThreshold, // Rejection threshold
     1686                        float gradLimit // Gradient limit
     1687                        );
     1688\end{verbatim}
     1689
     1690\code{pmRejectPixels} inspects those questionable \code{pixels}
     1691identified by \code{pmCombineImages} to determine if they are truly
     1692discrepant.  This inspection is performed in the coordinate frame of
     1693the detector, where the pixels haven't been smeared by transformation.
     1694Two tests are applied to each of the \code{images}:
     1695\begin{enumerate}
     1696\item The list of questionable pixels for an image is converted to an
     1697  image which is transformed back to the coordinate frame of the
     1698  detector.  Those pixels in the detector frame which have a value
     1699  exceeding \code{rejThreshold} are suspected cosmic rays and
     1700  subjected to the next test.  Depending on the value of the
     1701  \code{rejThreshold}, this test basically amounts to demanding that
     1702  questionable pixels neighbor each other in the transformed image.
     1703\item The cores of point sources may mimic a cosmic ray, especially in
     1704  under-sampled images.  To minimise flagging stars as cosmic rays, we
     1705  determine the gradient around the pixel of interest; if the gradient
     1706  is large, then the pixel is likely the core of a point source.  In
     1707  order to reliably measure the gradient in the presence of a
     1708  suspected cosmic ray, we use the companion images --- the gradient
     1709  is the mean gradient at the corresponding position on the other
     1710  images.  In order to calculate the corresponding positions, the
     1711  \code{inToOut} and \code{outToIn} transformations are required.  If
     1712  the gradient is less than \code{gradLimit}, then the pixel is
     1713  identified as a cosmic ray.
     1714\end{enumerate}
     1715
     1716The function shall return an array of \code{psPixels}, one for each of
     1717the input \code{images}, containing pixels that have been identified
     1718as cosmic rays according to the above criteria.
     1719
     1720If any of the input pointers are \code{NULL}, then the function shall
     1721generate an error and return \code{NULL}.
     1722
     1723\subsection{Example}
     1724
     1725Here is an example of what the image combination routine looks like,
     1726demonstrating how the various pieces fit together.  The inputs are:
     1727\begin{itemize}
     1728\item \code{psArray *inputs}: Input detector images, each a
     1729  \code{psImage} of type \code{psF32}
     1730\item \code{psArray *inputMask}: Input mask images, each a
     1731  \code{psImage} of type \code{psU8}
     1732\item \code{psArray *inputsErr}: Input error images, each a
     1733  \code{psImage} of type \code{psF32}
     1734\item \code{psPlaneTransform *skyToDetector}: Maps from sky
     1735  coordinates to detector coordinates, each a \code{psPlaneTransform}
     1736\item \code{psRegion *combineRegion}: Sky coordinate pixels to combine
     1737\item \code{int numIter}: Number of iterations in combination
     1738\item \code{float rejThreshold}: Threshold for rejection
     1739\item \code{float gradLimit}: Limit for gradient
     1740\end{itemize}
     1741
     1742The output is the combined image.
     1743
     1744\begin{verbatim}
     1745    psArray *transformed = psArrayAlloc(nImages); // Array of transformed images
     1746    psArray *transformedErr = psArrayAlloc(nImages); // Array of transformed error images
     1747    psArray *transformedMask = psArrayAlloc(nImages); // Array of masks for transformed images
     1748
     1749    for (int i = 0; i < nImages; i++) {
     1750        psPixels *blanks = NULL;        // List of blank pixels
     1751        transformed->data[i] = psImageTransform(NULL, &blanks, inputs->data[i],
     1752                                                inputMask->data[i], inputMaskVal, NAN, skyToDetector,
     1753                                                combineRegion, NULL, PS_INTERPOLATE_BILINEAR);
     1754        transformedErr->data[i] = psImageTransform(NULL, NULL, inputsErr->data[i], inputMask->data[i],
     1755                                                   inputMaskVal, NAN, skyToDetector, combineRegion, NULL,
     1756                                                   PS_INTERPOLATE_BILINEAR_VARIANCE);
     1757        psImage *skyImage = transformed->data[i]; // Dereference the transformed image
     1758        psRegion *blankRegion = psRegionAlloc(0, 0, skyImage->numCols, skyImage->numRows); // Size of
     1759                                                                                           // transformed
     1760                                                                                           // image
     1761        transformedMask->data[i] = psPixelsToMask(NULL, blanks, blankRegion, PS_MASK_BLANK);
     1762        psFree(blankRegion);
     1763        psFree(blanks);
     1764    }
     1765
     1766    psArray *rejected = NULL;           // Array of rejected pixel lists
     1767    psStats *combineStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistic to use in doing the combination
     1768    psImage *combined = pmCombineImages(NULL, &rejected, transformed, transformedErr, transformedMask, 0,
     1769                                        NULL, numIter, sigmaClip, combineStats); // Combined image
     1770    psArray *bad = pmRejectPixels(inputs, rejected, NULL, skyToDetector, rejThreshold, gradLimit); // Bad pix
     1771    psPixels *combinePixels = NULL;     // Pixels to combine
     1772    for (int i = 0; i < nImages; i++) {
     1773        psPixels *badSource = psPixelsTransform(NULL, bad->data[i], skyToDetector); // Bad pixels on the input
     1774        psImage *badMask = psPixelsToMask(NULL, badSource, PS_MASK_COSMICRAY); // Mask image for the input
     1775        (void)psBinaryOp(inputMask->data[i], inputMask->data[i], "|", badMask); // Put CRs into original mask
     1776        psFree(badSource);
     1777        psFree(badMask);
     1778
     1779        combinePixels = psPixelsConcatenate(redo, bad->data[i]);
     1780
     1781        // Update transformed image
     1782        psPixels *blanks = NULL;        // List of blank pixels
     1783        transformed->data[i] = psImageTransform(transformed->data[i], &blanks, inputs->data[i],
     1784                                                inputMask->data[i], inputMaskVal | PS_MASK_COSMICRAY, NAN,
     1785                                                skyToDetector, combineRegion, bad->data[i],
     1786                                                PS_INTERPOLATE_BILINEAR);
     1787        transformedErr->data[i] = psImageTransform(transformedErr->data[i], NULL, inputsErr->data[i],
     1788                                                   inputMask->data[i], inputMaskVal | PS_MASK_COSMICRAY,
     1789                                                   NAN, skyToDetector, combineRegion, bad->data[i],
     1790                                                   PS_INTERPOLATE_BILINEAR_VARIANCE);
     1791        psImage *skyImage = transformed->data[i]; // Dereference the transformed image
     1792        psRegion *blankRegion = psRegionAlloc(0, 0, skyImage->numCols, skyImage->numRows); // Size of
     1793                                                                                           // transformed
     1794                                                                                           // image
     1795        transformedMask->data[i] = psPixelsToMask(transformedMask->data[i], blanks, blankRegion,
     1796                                                  PS_MASK_BLANK);
     1797        psFree(blankRegion);
     1798        psFree(blanks);
     1799    }
     1800    psFree(bad);
     1801
     1802    // Combine with no rejection
     1803    combined = pmCombineImages(combined, NULL, transformed, transformedErr, transformedMask,
     1804                               PS_MASK_BLANK, combinePixels, 0, 0.0, combineStats);
     1805    psFree(combineStats);
     1806    psFree(combinePixels);
     1807    psFree(transformed);
     1808    psFree(transformedErr);
     1809    psFree(transformedMask);
     1810\end{verbatim}
     1811
     1812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     1813
     1814\section{Image Subtraction}
     1815
     1816Image subtraction is arguably the best method of identifying faint
     1817variable sources in images with different point-spread functions.  It
     1818relies on fitting for a convolution kernel that minimises the
     1819residuals in subtracting small regions of the image.  The use of a
     1820convolution kernel consisting of a linear combination of basis
     1821functions allows the problem to be solved with only modest computing
     1822power.
     1823
     1824\subsection{The kernels}
     1825
     1826We will allow for the use of two convolution kernels.  The first is
     1827that employed by the popular image subtraction program,
     1828\href{http://www2.iap.fr/users/alard/package.html}{ISIS}, consisting
     1829of Gaussians modified by polynomials:
     1830\begin{equation}
     1831B_{ijk}(u,v) = e^{(u^2 + v^2)/2\sigma_i^2} u^j v^k
     1832\end{equation}
     1833The second simply consists of delta functions, which we refer to as
     1834POIS (Pan-STARRS Optimal Image Subtraction):
     1835\begin{equation}
     1836B_{ij}(u,v) = \delta(u - i)\ \delta(v - j)
     1837\end{equation}
     1838\tbd{For further details, see the document about image subtraction for
     1839\PS{}.}  The former is widely used, while the second appears to be
     1840equally useful and faster, though not as tried and proven.
     1841
     1842\begin{verbatim}
     1843typedef enum {
     1844    PM_SUBTRACTION_KERNEL_POIS,         // POIS kernel --- delta functions
     1845    PM_SUBTRACTION_KERNEL_ISIS          // ISIS kernel --- gaussians modified by polynomials
     1846} pmSubtractionKernelsType;
     1847\end{verbatim}
     1848
     1849In order to simplify the book-keeping for the kernels, we will define
     1850a \code{pmSubtractionKernels}, which keeps track of the details of the
     1851each of the kernel basis functions:
     1852
     1853\begin{verbatim}
     1854typedef struct {
     1855    pmSubtractionKernelType type;       // Type of kernels --- allowing the use of multiple kernels
     1856    psVector *u, *v;                    // Offset (for POIS) or polynomial order (for ISIS)
     1857    psVector *sigma;                    // Width of Gaussian (for ISIS)
     1858    psVector *xOrder, *yOrder;          // Spatial polynomial order (for all)
     1859    int subIndex;                       // Index of kernel to be subtracted to maintain flux conservation
     1860    psArray *preCalc;                   // Array of images containing pre-calculated kernel (to
     1861                                        // accelerate ISIS; don't use for POIS)
     1862} pmSubtractionKernels;
     1863\end{verbatim}
     1864
     1865This structure caters for both choices of kernel type.  For a POIS
     1866kernel, the \code{u} and \code{v} vectors shall be set to the
     1867coordinates for the delta functions for the corresponding kernel.  For
     1868an ISIS kernel, the \code{sigma} vector shall be set to the Gaussian
     1869widths and the \code{u} and \code{v} vectors shall be set to the
     1870orders of the modifying polynomials for the corresponding kernel.  For
     1871both choices of kernel, the \code{xOrder} and \code{yOrder} vectors
     1872specify the order of the spatial variation.
     1873
     1874In order to maintain flux conservation when the kernel is spatially
     1875variable, we need to treat one kernel in the set differently.  The
     1876convolutions for this kernel, identified by the \code{subIndex}, are
     1877calculated in the usual way, while all others have the \code{subIndex}
     1878kernel subtracted from them.  For details, see the
     1879\href{http://www.edpsciences.org/journal/index.cfm?v_url=aas/full/2000/11/ds8706/ds8706.html}{paper
     1880by Alard (2000, A\&AS, 144, 363)}.
     1881
     1882Since the ISIS kernels are continuous functions, it is worth
     1883pre-calculating them instead of calculating them each time they are
     1884required.  The \code{preCalc} array, consisting of \code{psImage}s is
     1885provided for this purpose.
     1886
     1887The \code{pmSubtractionKernels} are generated by the following functions:
     1888
     1889\begin{verbatim}
     1890pmSubtractionKernels *pmSubtractionKernelsAllocPOIS(int size, int spatialOrder);
     1891pmSubtractionKernels *pmSubtractionKernelsAllocISIS(const psVector *sigmas, const psVector *orders,
     1892                                                    int size, int spatialOrder);
     1893\end{verbatim}
     1894
     1895\code{pmSubtractionKernelsAllocPOIS} shall generate the
     1896\code{pmSubtractionKernels} suitable for the POIS kernel basis set.
     1897This involves setting the \code{u}, \code{v}, \code{xOrder} and
     1898\code{yOrder} to the appropriate values.  \code{size} is the half-size
     1899of the kernel, and \code{spatialOrder} is the maximum spatial order
     1900(the spatial variation is $x^i y^j$ with $i+j <$ \code{spatialOrder}).
     1901The \code{subIndex} is set to the kernel which has \code{u = 0},
     1902\code{v = 0}, \code{xOrder = 0} and \code{yOrder = 0}.  There should
     1903be \code{(2 * size + 1) * (2 * size + 1) * (spatialOrder + 1) *
     1904(spatialOrder + 2) / 2} kernels.
     1905
     1906\code{pmSubtractionKernelsAllocISIS} shall generate the
     1907\code{pmSubtractionKernels} suitable for the ISIS kernel basis set.
     1908This involves setting the \code{sigma}, \code{u}, \code{v},
     1909\code{xOrder} and \code{yOrder} to the appropriate values, as well as
     1910generating the \code{preCalc} images.  Note that the \code{sigma}
     1911vector contained within the \code{pmSubtractionKernels} is not the
     1912same as the input \code{sigmas} vector, but contains repeated entries.
     1913\code{size} is the half-size of the kernel, which specifies the size
     1914of the \code{preCalc} images.  The \code{spatialOrder} is the maximum
     1915spatial order (the spatial variation is $x^i y^j$ with $i+j <$
     1916\code{spatialOrder}).  The \code{subIndex} is set to the kernel which
     1917has \code{u = 0}, \code{v = 0}, \code{xOrder = 0} and \code{yOrder =
     19180}, for the first of the gaussian widths in the \code{sigmas} vector.
     1919
     1920\subsection{Stamps}
     1921
     1922Sub-regions on an image which are used to derive the best-fit
     1923convolution kernel are referred to as ``stamps''.
     1924
     1925\begin{verbatim}
     1926typedef struct {
     1927    int x, y;                           // Position
     1928    psImage *matrix;                    // Associated matrix
     1929    psVector *vector;                   // Associated vector
     1930    psStampStatus status;               // Status of stamp
     1931} pmStamp;
     1932\end{verbatim}
     1933
     1934A stamp is the region around a central pixel, \code{x,y}.  The
     1935\code{matrix} and \code{vector} are generated in the process of
     1936solving for the best-fit convolution kernel; each of these will likely
     1937be of type \code{psF64} in order to maintain the best possible
     1938precision (we will be summing squares).  In order to allow us to throw
     1939out stamps without having to laboriously recompute the total
     1940least-squares matrix and vector, we use a separate matrix and vector
     1941for each stamp.
     1942
     1943To allow iteration on the choice of stamps, a stamp contains a
     1944\code{status}, an enumerated type:
     1945
     1946\begin{verbatim}
     1947typedef enum {
     1948    PM_STAMP_USED,                      // Use this stamp
     1949    PM_STAMP_REJECTED,                  // This stamp has been rejected
     1950    PM_STAMP_RECALC,                    // Having been reset, this stamp needs to be recalculated
     1951    PM_STAMP_NONE                       // No stamp in this region
     1952} pmStampStatus;
     1953\end{verbatim}
     1954
     1955\begin{verbatim}
     1956psArary *pmSubtractionFindStamps(psArray *stamps, // Output stamps, or NULL
     1957                                 const psImage *image, // Image for which to find stamps
     1958                                 const psImage *mask, // Mask
     1959                                 unsigned int maskVal, // Value for mask
     1960                                 float threshold, // Threshold for stamps in the image
     1961                                 int xNum, int yNum, // Number of stamps in x and y
     1962                                 int border // Border around image to ignore (should be size of kernel)
     1963                                 );
     1964\end{verbatim}
     1965
     1966\code{pmSubtractionFindStamps} returns an array of stamps on the
     1967\code{image} suitable for use in calculating the best-fit convolution
     1968kernel.  Except for a \code{border} all the way around, the
     1969\code{image} is broken into \code{xNum} $\times$ \code{yNum}
     1970rectangles; there will be a stamp within each rectangle.  If
     1971\code{stamps} is non-\code{NULL}, then the function shall only attempt
     1972to identify a new stamp in a particular rectangle if the corresponding
     1973stamp \code{status} is \code{PM_STAMP_REJECTED}.
     1974
     1975A stamp shall be recognised as the pixel with the greatest value that
     1976does not have the corresponding pixel in the \code{mask} matching
     1977\code{maskVal}.  If the value of the this pixel does not exceed
     1978\code{threshold}, then the stamp \code{status} shall be marked as
     1979\code{PM_STAMP_NONE}, which means that the stamp will be ignored in
     1980future iterations.  If a legitimate stamp is found within the region,
     1981then its status shall be changed to \code{PM_STAMP_RECALC}.
     1982
     1983
     1984\subsection{Solving for the kernel}
     1985
     1986Calculating the best-fit convolution kernel requires solving a matrix
     1987equation, the elements of which are obtained by applying the kernel
     1988basis functions to the stamps.  The final matrix and vector are the
     1989sum of the matrices and vectors obtained for each of the individual
     1990stamps.
     1991
     1992\begin{verbatim}
     1993bool pmSubtractionCalculateEquation(psArray *stamps, // The stamps for which to calculate the equation
     1994                                    const psImage *reference, // Reference image
     1995                                    const psImage *input, // Input image
     1996                                    const psSubtractionKernels *kernels, // The kernel basis functions
     1997                                    int footprint // Half-size of region over which to calculate equation
     1998                                    );
     1999\end{verbatim}
     2000
     2001\code{pmSubtractionCalculateEquation} shall calculate the
     2002\code{matrix} and \code{vector} for each of the \code{stamps} which
     2003have \code{status} set to \code{PM_STAMP_RECALC}.  The calculation is
     2004made over a region with a half size of \code{footprint} on the
     2005\code{reference} and \code{input} images, using each of the
     2006\code{kernels}.  In the event that any of the input pointers are
     2007\code{NULL}, the function shall generate an error and return
     2008\code{false}; otherwise, the function shall return \code{true}.
     2009
     2010The vector is:
     2011\begin{equation}
     2012v_i = \sum_{x,y} I(x,y) [ R(x,y) \otimes B_i(u,v) ] / \sigma(x,y)^2
     2013\end{equation}
     2014and the matrix is:
     2015\begin{equation}
     2016M_{ij} = \sum_{x,y} \left[ R(x,y) \otimes B_i(u,v) \right] \  \left[ R(x,y) \otimes B_j(u,v) \right] / \sigma(x,y)^2
     2017\end{equation}
     2018where $I(x,y)$ is the input image, $R(x,y)$ is the reference image,
     2019$B_i(u,v)$ is the $i$-th kernel basis function, $\otimes$ denotes
     2020convolution, $\sigma(x,y) = R(x,y)^{1/2}$ is an estimate of the error,
     2021and the sum over $x,y$ indicates summing over the stamp regions.
     2022
     2023In addition to the each of the \code{kernels}, an additional parameter
     2024for which we must solve is the difference in the background level
     2025between the \code{reference} and \code{input} images.  The appropriate
     2026term shall be added to the \code{matrix} and \code{vector}.
     2027
     2028In order to maintain flux conservation when the kernel is spatially
     2029variable, for each of the kernel basis functions apart from the first,
     2030the kernel actually employed shall be the first kernel function
     2031subtracted from the original kernel function.
     2032
     2033Having calculated the matrix equation for a stamp, its \code{status}
     2034is set to \code{PM_STAMP_USED}.
     2035
     2036Since this step is one of the major rate-limiting factors in image
     2037subtraction, care should be taken with optimisation.
     2038
     2039\begin{verbatim}
     2040psVector *pmSubtractionSolveEquation(psVector *solution,        // Solution vector, or NULL
     2041                                     const psArray *stamps // Array of stamps
     2042                                     );
     2043\end{verbatim}
     2044
     2045\code{pmSubtractionSolveEquation} shall solve the matrix equation
     2046provided by each of the \code{stamps}, returning the \code{solution}
     2047vector.  This involves summing the \code{matrix} and \code{vector} of
     2048each of the stamps which have \code{status} set to
     2049\code{PM_STAMP_USED}, and multiplying the inverse of the matrix by the
     2050\code{vector}.  If the \code{solution} is \code{NULL}, then the
     2051function shall allocate and return a new vector; otherwise, the
     2052\code{solution} vector shall be modified in-place.  If \code{stamps}
     2053is \code{NULL}, then the function shall generate an error and return
     2054\code{NULL}.  The type of the \code{solution} vector should be
     2055\code{psF64}, since the matrix equation involves summing squares.
     2056
     2057
     2058\subsection{Rejection of stamps}
     2059
     2060\begin{verbatim}
     2061bool pmSubtractionRejectStamps(psArray *stamps, // Array of stamps to check for rejection
     2062                               psImage *mask, // Mask image
     2063                               unsigned int badStampMaskVal, // Value to use in mask for bad stamp
     2064                               int footprint, // Region to mask if stamp is bad
     2065                               float sigmaRej, // Number of RMS deviations above zero at which to reject
     2066                               const psImage *refImage, // Reference image
     2067                               const psImage *inImage, // Input image
     2068                               const psVector *solution, // Solution vector
     2069                               const pmSubtractionKernels *kernels // Array of kernel parameters
     2070                               );
     2071\end{verbatim}
     2072
     2073\code{pmSubtractionRejectStamps} shall apply the \code{solution} to
     2074the \code{stamps}, rejecting stamps for which the mean square
     2075residuals exceed \code{sigmaRej} RMS deviations from zero.
     2076\code{stamps} which are rejected have their \code{status} set to
     2077\code{PM_STAMP_REJECTED}, and have pixels within \code{footprint} of
     2078the corresponding position in the \code{mask} set to
     2079\code{badStampMaskVal} so they will not be used again.
     2080
     2081The deviations are calculated through extracting the stamps from the
     2082\code{refImage} and \code{inImage}, convolving the reference stamp by
     2083the best-fit kernel (derived from the \code{solutions} vector and the
     2084\code{kernels}), subtracting and then dividing by the stamp from the
     2085input image, and then squaring to obtain the mean square residual.
     2086
     2087\subsection{Visualisation of kernel}
     2088
     2089Having solved for the best-fit kernel, it is often useful to visualise
     2090it.
     2091
     2092\begin{verbatim}
     2093psImage *pmSubtractionKernelImage(psImage *out, const psVector *solution,
     2094                                  const pmSubtractionKernels *kernels, float x, float y);
     2095\end{verbatim}
     2096
     2097\code{pmSubtractionKernelImage} shall create an image of the kernel
     2098from the \code{solution} vector and the \code{kernels}.  The relative
     2099position (between -1 and +1) on the image at which to evaluate the
     2100kernel (important if the kernel is spatially variable) is specified by
     2101\code{x} and \code{y}.  If \code{out} is \code{NULL}, then the
     2102function shall allocate a new image of sufficient size, and return the
     2103result; otherwise, \code{out} shall be modified in-place.
     2104
     2105
     2106\subsection{Example}
     2107
     2108Here is an example of what the image subtraction routine looks like,
     2109demonstrating how the various pieces fit together.  The inputs are:
     2110\begin{itemize}
     2111\item \code{psImage *reference}: Reference image
     2112\item \code{psImage *refMask}: Mask for reference image
     2113\item \code{psImage *input}: Input image
     2114\item \code{psImage *inMask}: Mask for input image
     2115\item \code{unsigned int maskVal}: Value to be masked
     2116\item \code{pmSubtractionKernelType kernelType}: Type of kernel to use
     2117\item \code{int kernelHalfSize}: Half the kernel size (full size is \code{2*kernelHalfSize + 1})
     2118\item \code{psVector *sigmas}: Widths for the ISIS Gaussians
     2119\item \code{psVector *polyOrders}: Polynomial orders for ISIS Gaussians
     2120\item \code{int spatialOrder}: Maximum spatial order for spatially variable kernel
     2121\item \code{float stampThreshold}: Threshold for finding stamps
     2122\item \code{int nStampsX, nStampsY}: Number of stamps in x and y
     2123\item \code{int stampSize}: Half size of stamp footprint
     2124\item \code{int numIter}: Number of iterations on the stamps
     2125\item \code{float sigmaRej}: Rejection threshold for stamps
     2126\end{itemize}
     2127
     2128The output is the subtracted image and the corresponding mask.
     2129
     2130\begin{verbatim}
     2131    // Mask around bad pixels in the reference image.  There are two cases to worry about:
     2132    // 1. Bad pixels within the kernel, which will affect the subtracted image
     2133    // 2. Bad pixels within the stamp, which affects the calculation of the kernel
     2134    psImage *subMask = psImageGrowMask(NULL, refMask, maskVal, kernelHalfSize, PS_MASK_NEAR_BAD);
     2135    (void)psImageGrowMask(subMask, refMask, maskVal, stampSize, PS_MASK_BAD_STAMP);
     2136    // Add in the mask for the input image.  Don't need to grow this, since it isn't convolved.
     2137    (void)psBinaryOp(subMask, subMask, "|", inMask);
     2138
     2139    // Generate kernel basis functions
     2140    psArray *kernels = NULL;            // Array of kernel basis functions
     2141    switch (kernelType) {
     2142      case PM_SUBTRACTION_KERNEL_POIS:
     2143        // Create the kernel basis functions
     2144        kernels = pmSubtractionKernelsGeneratePOIS(kernelHalfSize, spatialOrder);
     2145        break;
     2146      case PM_SUBTRACTION_KERNEL_ISIS:
     2147        kernels = pmSubtractionKernelsGenerateISIS(sigmas, polyOrders, kernelHalfSize, spatialOrder);
     2148        break;
     2149      default:
     2150        barf();
     2151    }
     2152
     2153    psArray *stamps = NULL;             // Array of stamps
     2154    psVector *kernelCoeffs = NULL;      // Coefficients for the kernels
     2155    bool rejected = true;               // Did we reject a stamp in the last iteration?
     2156
     2157    // Iterate for a solution
     2158    for (int iter = 0; iter < numIter && rejected; iter++) {
     2159
     2160        // Find stamps
     2161        stamps = pmSubtractionFindStamps(stamps, reference, subMask, maskVal | PS_MASK_BAD_STAMP,
     2162                                         stampThreshold, nStampsX, nStampsY, stampSize, kernelHalfSize);
     2163
     2164        // Generate and solve matrix equations
     2165        (void)pmSubtractionCalculateEquation(stamps, reference, input, kernels, stampSize);
     2166        kernelCoeffs = pmSubtractionSolveEquation(kernelCoeffs, stamps);
     2167
     2168        // Reject bad stamps
     2169        rejected = pmSubtractionRejectStamps(stamps, subMask, PS_MASK_BAD_STAMP, stampSize, sigmaRej,
     2170                                             reference, input, kernelCoeffs, kernels);
     2171    }
     2172
     2173    // Convolve the reference image
     2174    psImage *referenceConvolved = pmSubtractionConvolveImage(NULL, reference, subMask, kernelCoeffs, kernels);
     2175    // Subtract
     2176    psImage *subtracted = (psImage*)psBinaryOp(NULL, input, "-", referenceConvolved);
     2177
     2178    // What does the kernel look like?
     2179    psImage *kernelImage = pmSubtractionKernelImage(NULL, kernelCoeffs, kernels, 0.0, 0.0);
     2180    // Check/save kernel image, print statistics....
     2181
     2182    psFree(referenceConvolved);
     2183    psFree(stamps);
     2184    psFree(kernels);
     2185    psFree(kernelCoeffs);
     2186\ebd{verbatim}
     2187
     2188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     2189
    16172190\section{Revision Change Log}
    16182191\input{ChangeLogSDRS.tex}
Note: See TracChangeset for help on using the changeset viewer.