Changeset 3425 for trunk/doc/modules/ModulesSDRS.tex
- Timestamp:
- Mar 15, 2005, 1:13:16 PM (21 years ago)
- File:
-
- 1 edited
-
trunk/doc/modules/ModulesSDRS.tex (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/doc/modules/ModulesSDRS.tex
r3300 r3425 1 %%% $Id: ModulesSDRS.tex,v 1.3 2 2005-02-22 19:30:45 eugene Exp $1 %%% $Id: ModulesSDRS.tex,v 1.33 2005-03-15 23:13:16 price Exp $ 2 2 \documentclass[panstarrs]{panstarrs} 3 3 … … 1615 1615 \end{verbatim} 1616 1616 1617 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1618 1619 \section{Image Combination} 1620 1621 The image combination for \PS{} will employ an iterative approach, in 1622 order to identify cosmic rays. The first pass involves transforming 1623 and combining the input images, and noting pixels which are apparently 1624 deviant. These pixels are examined in further detail, before a subset 1625 of them are declared to be bad, whereupon these pixels are 1626 re-transformed, and the images are combined properly. Here we 1627 introduce two functions which will perform the combination and 1628 examination 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} 1635 psImage *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}, 1649 returning the \code{combined} image and a list of 1650 \code{questionablePixels} in each input image. The array of error 1651 images, \code{errors}, shall be used to calculate the value in the 1652 combined image and the list of questionable pixels, if 1653 non-\code{NULL}. Pixels whose corresponding value in the array of 1654 mask images, \code{masks}, matches \code{maskVal} shall be masked from 1655 the combination. The \code{images}, \code{errors} and \code{masks} 1656 arrays, if non-\code{NULL}, shall all carry the same number of images; 1657 otherwise the function shall generate an error and return \code{NULL}. 1658 The sizes of all images in the \code{images}, \code{errors} and 1659 \code{masks} arrays shall be identical; otherwise the function shall 1660 generate an error and return \code{NULL}. 1661 1662 If \code{pixels} is non-\code{NULL}, only those pixels specified shall 1663 be combined. The combination consists of \code{numIter} iterations in 1664 which a stack of pixels is combined using the specified \code{stats}. 1665 In each iteration, questionable pixels are identified as lying more 1666 than \code{sigmaClip} standard deviations from the combined value; 1667 these pixels are excluded from the stack for the next iteration. The 1668 value for the combined image is that produced by the \textit{first} 1669 iteration (i.e., with no pixels excluded except those which have their 1670 corresponding mask match the \code{maskVal}); this allows subsequent 1671 calls to the function to only act on a small fraction of the pixels, 1672 since questionable pixels identified in the first call of the function 1673 will be properly rejected at a later point (see the example, below). 1674 1675 In the event that \code{images} or \code{stats} are \code{NULL}, the 1676 function shall generate an error and return \code{NULL}. 1677 1678 \subsection{Rejecting pixels} 1679 1680 \begin{verbatim} 1681 psArray *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} 1691 identified by \code{pmCombineImages} to determine if they are truly 1692 discrepant. This inspection is performed in the coordinate frame of 1693 the detector, where the pixels haven't been smeared by transformation. 1694 Two 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 1716 The function shall return an array of \code{psPixels}, one for each of 1717 the input \code{images}, containing pixels that have been identified 1718 as cosmic rays according to the above criteria. 1719 1720 If any of the input pointers are \code{NULL}, then the function shall 1721 generate an error and return \code{NULL}. 1722 1723 \subsection{Example} 1724 1725 Here is an example of what the image combination routine looks like, 1726 demonstrating 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 1742 The 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 1816 Image subtraction is arguably the best method of identifying faint 1817 variable sources in images with different point-spread functions. It 1818 relies on fitting for a convolution kernel that minimises the 1819 residuals in subtracting small regions of the image. The use of a 1820 convolution kernel consisting of a linear combination of basis 1821 functions allows the problem to be solved with only modest computing 1822 power. 1823 1824 \subsection{The kernels} 1825 1826 We will allow for the use of two convolution kernels. The first is 1827 that employed by the popular image subtraction program, 1828 \href{http://www2.iap.fr/users/alard/package.html}{ISIS}, consisting 1829 of Gaussians modified by polynomials: 1830 \begin{equation} 1831 B_{ijk}(u,v) = e^{(u^2 + v^2)/2\sigma_i^2} u^j v^k 1832 \end{equation} 1833 The second simply consists of delta functions, which we refer to as 1834 POIS (Pan-STARRS Optimal Image Subtraction): 1835 \begin{equation} 1836 B_{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 1840 equally useful and faster, though not as tried and proven. 1841 1842 \begin{verbatim} 1843 typedef 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 1849 In order to simplify the book-keeping for the kernels, we will define 1850 a \code{pmSubtractionKernels}, which keeps track of the details of the 1851 each of the kernel basis functions: 1852 1853 \begin{verbatim} 1854 typedef 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 1865 This structure caters for both choices of kernel type. For a POIS 1866 kernel, the \code{u} and \code{v} vectors shall be set to the 1867 coordinates for the delta functions for the corresponding kernel. For 1868 an ISIS kernel, the \code{sigma} vector shall be set to the Gaussian 1869 widths and the \code{u} and \code{v} vectors shall be set to the 1870 orders of the modifying polynomials for the corresponding kernel. For 1871 both choices of kernel, the \code{xOrder} and \code{yOrder} vectors 1872 specify the order of the spatial variation. 1873 1874 In order to maintain flux conservation when the kernel is spatially 1875 variable, we need to treat one kernel in the set differently. The 1876 convolutions for this kernel, identified by the \code{subIndex}, are 1877 calculated in the usual way, while all others have the \code{subIndex} 1878 kernel 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 1880 by Alard (2000, A\&AS, 144, 363)}. 1881 1882 Since the ISIS kernels are continuous functions, it is worth 1883 pre-calculating them instead of calculating them each time they are 1884 required. The \code{preCalc} array, consisting of \code{psImage}s is 1885 provided for this purpose. 1886 1887 The \code{pmSubtractionKernels} are generated by the following functions: 1888 1889 \begin{verbatim} 1890 pmSubtractionKernels *pmSubtractionKernelsAllocPOIS(int size, int spatialOrder); 1891 pmSubtractionKernels *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. 1897 This involves setting the \code{u}, \code{v}, \code{xOrder} and 1898 \code{yOrder} to the appropriate values. \code{size} is the half-size 1899 of the kernel, and \code{spatialOrder} is the maximum spatial order 1900 (the spatial variation is $x^i y^j$ with $i+j <$ \code{spatialOrder}). 1901 The \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 1903 be \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. 1908 This involves setting the \code{sigma}, \code{u}, \code{v}, 1909 \code{xOrder} and \code{yOrder} to the appropriate values, as well as 1910 generating the \code{preCalc} images. Note that the \code{sigma} 1911 vector contained within the \code{pmSubtractionKernels} is not the 1912 same as the input \code{sigmas} vector, but contains repeated entries. 1913 \code{size} is the half-size of the kernel, which specifies the size 1914 of the \code{preCalc} images. The \code{spatialOrder} is the maximum 1915 spatial order (the spatial variation is $x^i y^j$ with $i+j <$ 1916 \code{spatialOrder}). The \code{subIndex} is set to the kernel which 1917 has \code{u = 0}, \code{v = 0}, \code{xOrder = 0} and \code{yOrder = 1918 0}, for the first of the gaussian widths in the \code{sigmas} vector. 1919 1920 \subsection{Stamps} 1921 1922 Sub-regions on an image which are used to derive the best-fit 1923 convolution kernel are referred to as ``stamps''. 1924 1925 \begin{verbatim} 1926 typedef 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 1934 A stamp is the region around a central pixel, \code{x,y}. The 1935 \code{matrix} and \code{vector} are generated in the process of 1936 solving for the best-fit convolution kernel; each of these will likely 1937 be of type \code{psF64} in order to maintain the best possible 1938 precision (we will be summing squares). In order to allow us to throw 1939 out stamps without having to laboriously recompute the total 1940 least-squares matrix and vector, we use a separate matrix and vector 1941 for each stamp. 1942 1943 To allow iteration on the choice of stamps, a stamp contains a 1944 \code{status}, an enumerated type: 1945 1946 \begin{verbatim} 1947 typedef 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} 1956 psArary *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 1968 kernel. Except for a \code{border} all the way around, the 1969 \code{image} is broken into \code{xNum} $\times$ \code{yNum} 1970 rectangles; there will be a stamp within each rectangle. If 1971 \code{stamps} is non-\code{NULL}, then the function shall only attempt 1972 to identify a new stamp in a particular rectangle if the corresponding 1973 stamp \code{status} is \code{PM_STAMP_REJECTED}. 1974 1975 A stamp shall be recognised as the pixel with the greatest value that 1976 does 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 1980 future iterations. If a legitimate stamp is found within the region, 1981 then its status shall be changed to \code{PM_STAMP_RECALC}. 1982 1983 1984 \subsection{Solving for the kernel} 1985 1986 Calculating the best-fit convolution kernel requires solving a matrix 1987 equation, the elements of which are obtained by applying the kernel 1988 basis functions to the stamps. The final matrix and vector are the 1989 sum of the matrices and vectors obtained for each of the individual 1990 stamps. 1991 1992 \begin{verbatim} 1993 bool 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 2003 have \code{status} set to \code{PM_STAMP_RECALC}. The calculation is 2004 made 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 2010 The vector is: 2011 \begin{equation} 2012 v_i = \sum_{x,y} I(x,y) [ R(x,y) \otimes B_i(u,v) ] / \sigma(x,y)^2 2013 \end{equation} 2014 and the matrix is: 2015 \begin{equation} 2016 M_{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} 2018 where $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 2020 convolution, $\sigma(x,y) = R(x,y)^{1/2}$ is an estimate of the error, 2021 and the sum over $x,y$ indicates summing over the stamp regions. 2022 2023 In addition to the each of the \code{kernels}, an additional parameter 2024 for which we must solve is the difference in the background level 2025 between the \code{reference} and \code{input} images. The appropriate 2026 term shall be added to the \code{matrix} and \code{vector}. 2027 2028 In order to maintain flux conservation when the kernel is spatially 2029 variable, for each of the kernel basis functions apart from the first, 2030 the kernel actually employed shall be the first kernel function 2031 subtracted from the original kernel function. 2032 2033 Having calculated the matrix equation for a stamp, its \code{status} 2034 is set to \code{PM_STAMP_USED}. 2035 2036 Since this step is one of the major rate-limiting factors in image 2037 subtraction, care should be taken with optimisation. 2038 2039 \begin{verbatim} 2040 psVector *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 2046 provided by each of the \code{stamps}, returning the \code{solution} 2047 vector. This involves summing the \code{matrix} and \code{vector} of 2048 each 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 2051 function shall allocate and return a new vector; otherwise, the 2052 \code{solution} vector shall be modified in-place. If \code{stamps} 2053 is \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} 2061 bool 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 2074 the \code{stamps}, rejecting stamps for which the mean square 2075 residuals 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 2078 the corresponding position in the \code{mask} set to 2079 \code{badStampMaskVal} so they will not be used again. 2080 2081 The deviations are calculated through extracting the stamps from the 2082 \code{refImage} and \code{inImage}, convolving the reference stamp by 2083 the best-fit kernel (derived from the \code{solutions} vector and the 2084 \code{kernels}), subtracting and then dividing by the stamp from the 2085 input image, and then squaring to obtain the mean square residual. 2086 2087 \subsection{Visualisation of kernel} 2088 2089 Having solved for the best-fit kernel, it is often useful to visualise 2090 it. 2091 2092 \begin{verbatim} 2093 psImage *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 2098 from the \code{solution} vector and the \code{kernels}. The relative 2099 position (between -1 and +1) on the image at which to evaluate the 2100 kernel (important if the kernel is spatially variable) is specified by 2101 \code{x} and \code{y}. If \code{out} is \code{NULL}, then the 2102 function shall allocate a new image of sufficient size, and return the 2103 result; otherwise, \code{out} shall be modified in-place. 2104 2105 2106 \subsection{Example} 2107 2108 Here is an example of what the image subtraction routine looks like, 2109 demonstrating 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 2128 The 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 1617 2190 \section{Revision Change Log} 1618 2191 \input{ChangeLogSDRS.tex}
Note:
See TracChangeset
for help on using the changeset viewer.
