Coverage for python/lsst/ip/diffim/snapPsfMatch.py: 50%
30 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-24 03:48 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-24 03:48 -0700
1# This file is part of ip_diffim.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22__all__ = ["SnapPsfMatchConfigDF", "SnapPsfMatchConfigAL", "SnapPsfMatchConfig", "SnapPsfMatchTask"]
24import lsst.pex.config as pexConfig
25from .psfMatch import PsfMatchConfigDF, PsfMatchConfigAL
26from .imagePsfMatch import ImagePsfMatchTask, ImagePsfMatchConfig
29class SnapPsfMatchConfigDF(PsfMatchConfigDF):
30 """Delta-function Psf-matching config optimized for snap subtraction"""
32 def setDefaults(self):
33 PsfMatchConfigDF.setDefaults(self)
35 # No regularization
36 self.useRegularization = False
38 # Pca
39 self.usePcaForSpatialKernel = True
40 self.subtractMeanForPca = True
41 self.numPrincipalComponents = 5
44class SnapPsfMatchConfigAL(PsfMatchConfigAL):
45 """Sum-of-Gaussian (Alard-Lupton) Psf-matching config optimized for snap subtraction"""
47 def setDefaults(self):
48 PsfMatchConfigAL.setDefaults(self)
50 # Simple basis set
51 self.alardNGauss = 2
52 self.alardDegGauss = (4, 2)
53 self.alardSigGauss = (1.0, 2.5)
56class SnapPsfMatchConfig(ImagePsfMatchConfig):
57 kernel = pexConfig.ConfigChoiceField(
58 doc="kernel type",
59 typemap=dict(
60 AL=SnapPsfMatchConfigAL,
61 DF=SnapPsfMatchConfigDF
62 ),
63 default="AL",
64 )
66 doWarping = pexConfig.Field(
67 dtype=bool,
68 doc="Warp the snaps?",
69 default=False
70 )
72 def setDefaults(self):
73 ImagePsfMatchConfig.setDefaults(self)
75 # No spatial variation in model
76 self.kernel.active.spatialKernelOrder = 0
78 # Don't fit for differential background
79 self.kernel.active.fitForBackground = False
81 # Small kernel size
82 self.kernel.active.kernelSize = 7
84 # With zero spatial order don't worry about spatial clipping
85 self.kernel.active.spatialKernelClipping = False
88class SnapPsfMatchTask(ImagePsfMatchTask):
89 """Image-based Psf-matching of two subsequent snaps from the same visit
91 Notes
92 -----
93 This Task differs from ImagePsfMatchTask in that it matches two Exposures assuming that the images have
94 been acquired very closely in time. Under this assumption, the astrometric misalignments and/or
95 relative distortions should be within a pixel, and the Psf-shapes should be very similar. As a
96 consequence, the default configurations for this class assume a very simple solution.
98 - The spatial variation in the kernel (SnapPsfMatchConfig.spatialKernelOrder) is assumed to be zero
100 - With no spatial variation, we turn of the spatial
101 clipping loops (SnapPsfMatchConfig.spatialKernelClipping)
103 - The differential background is not fit for (SnapPsfMatchConfig.fitForBackground)
105 - The kernel is expected to be appx.
106 a delta function, and has a small size (SnapPsfMatchConfig.kernelSize)
108 The sub-configurations for the Alard-Lupton (SnapPsfMatchConfigAL)
109 and delta-function (SnapPsfMatchConfigDF)
110 bases also are designed to generate a small, simple kernel.
112 Task initialization
114 Initialization is the same as base class ImagePsfMatch.__init__,
115 with the difference being that the Task's
116 ConfigClass is SnapPsfMatchConfig.
118 Invoking the Task
120 The Task is only configured to have a subtractExposures method, which in turn calls
121 ImagePsfMatchTask.subtractExposures.
123 Configuration parameters
125 See SnapPsfMatchConfig, which uses either SnapPsfMatchConfigDF and SnapPsfMatchConfigAL
126 as its active configuration.
128 Debug variables
130 The ``pipetask`` command line interface supports a
131 flag --debug to import @b debug.py from your PYTHONPATH. The relevant contents of debug.py
132 for this Task include:
134 .. code-block:: py
136 import sys
137 import lsstDebug
138 def DebugInfo(name):
139 di = lsstDebug.getInfo(name)
140 if name == "lsst.ip.diffim.psfMatch":
141 di.display = True # enable debug output
142 di.maskTransparency = 80 # display mask transparency
143 di.displayCandidates = True # show all the candidates and residuals
144 di.displayKernelBasis = False # show kernel basis functions
145 di.displayKernelMosaic = True # show kernel realized across the image
146 di.plotKernelSpatialModel = False # show coefficients of spatial model
147 di.showBadCandidates = True # show the bad candidates (red) along with good (green)
148 elif name == "lsst.ip.diffim.imagePsfMatch":
149 di.display = True # enable debug output
150 di.maskTransparency = 30 # display mask transparency
151 di.displayTemplate = True # show full (remapped) template
152 di.displaySciIm = True # show science image to match to
153 di.displaySpatialCells = True # show spatial cells
154 di.displayDiffIm = True # show difference image
155 di.showBadCandidates = True # show the bad candidates (red) along with good (green)
156 elif name == "lsst.ip.diffim.diaCatalogSourceSelector":
157 di.display = False # enable debug output
158 di.maskTransparency = 30 # display mask transparency
159 di.displayExposure = True # show exposure with candidates indicated
160 di.pauseAtEnd = False # pause when done
161 return di
162 lsstDebug.Info = DebugInfo
163 lsstDebug.frame = 1
165 Note that if you want addional logging info, you may add to your scripts:
167 .. code-block:: py
169 import lsst.utils.logging as logUtils
170 logUtils.trace_set_at("lsst.ip.diffim", 4)
172 Examples
173 --------
174 First, create a subclass of SnapPsfMatchTask that accepts two exposures.
175 Ideally these exposures would have been taken back-to-back,
176 such that the pointing/background/Psf does not vary substantially between the two:
178 .. code-block:: py
180 class MySnapPsfMatchTask(SnapPsfMatchTask):
181 def __init__(self, *args, **kwargs):
182 SnapPsfMatchTask.__init__(self, *args, **kwargs)
183 def run(self, templateExp, scienceExp):
184 return self.subtractExposures(templateExp, scienceExp)
186 And allow the user the freedom to either run the script in default mode,
187 or point to their own images on disk. Note that these images must be
188 readable as an lsst.afw.image.Exposure
190 .. code-block:: py
192 if __name__ == "__main__":
193 import argparse
194 parser = argparse.ArgumentParser(description="Demonstrate the use of ImagePsfMatchTask")
195 parser.add_argument("--debug", "-d", action="store_true", help="Load debug.py?", default=False)
196 parser.add_argument("--template", "-t", help="Template Exposure to use", default=None)
197 parser.add_argument("--science", "-s", help="Science Exposure to use", default=None)
198 args = parser.parse_args()
200 We have enabled some minor display debugging in this script via the –debug option. However,
201 if you have an lsstDebug debug.in your PYTHONPATH you will get additional debugging displays.
202 The following block checks for this script
204 .. code-block:: py
206 if args.debug:
207 try:
208 import debug
209 # Since I am displaying 2 images here, set the starting frame number for the LSST debug LSST
210 debug.lsstDebug.frame = 3
211 except ImportError as e:
212 print(e, file=sys.stderr)
214 Finally, we call a run method that we define below.
215 First set up a Config and choose the basis set to use:
217 .. code-block:: py
219 def run(args):
220 #
221 # Create the Config and use sum of gaussian basis
222 #
223 config = SnapPsfMatchTask.ConfigClass()
224 config.doWarping = True
225 config.kernel.name = "AL"
227 Make sure the images (if any) that were sent to the script exist on disk and are readable.
228 If no images are sent, make some fake data up for the sake of this example script
229 (have a look at the code if you want more details on generateFakeImages;
230 as a detail of how the fake images were made, you do have to fit for a differential background):
232 .. code-block:: py
234 # Run the requested method of the Task
235 if args.template is not None and args.science is not None:
236 if not os.path.isfile(args.template):
237 raise FileNotFoundError("Template image %s does not exist" % (args.template))
238 if not os.path.isfile(args.science):
239 raise FileNotFoundError("Science image %s does not exist" % (args.science))
240 try:
241 templateExp = afwImage.ExposureF(args.template)
242 except Exception as e:
243 raise RuntimeError("Cannot read template image %s" % (args.template))
244 try:
245 scienceExp = afwImage.ExposureF(args.science)
246 except Exception as e:
247 raise RuntimeError("Cannot read science image %s" % (args.science))
248 else:
249 templateExp, scienceExp = generateFakeImages()
250 config.kernel.active.fitForBackground = True
251 config.kernel.active.spatialBgOrder = 0
252 config.kernel.active.sizeCellX = 128
253 config.kernel.active.sizeCellY = 128
255 Display the two images if -debug
257 .. code-block:: py
259 if args.debug:
260 afwDisplay.Display(frame=1).mtv(templateExp, title="Example script: Input Template")
261 afwDisplay.Display(frame=2).mtv(scienceExp, title="Example script: Input Science Image")
263 Create and run the Task
265 .. code-block:: py
267 # Create the Task
268 psfMatchTask = MySnapPsfMatchTask(config=config)
269 # Run the Task
270 result = psfMatchTask.run(templateExp, scienceExp)
272 And finally provide optional debugging display of the Psf-matched (via the Psf models) science image:
274 .. code-block:: py
276 if args.debug:
277 # See if the LSST debug has incremented the frame number; if not start with frame 3
278 try:
279 frame = debug.lsstDebug.frame + 1
280 except Exception:
281 frame = 3
282 afwDisplay.Display(frame=frame).mtv(result.matchedExposure,
283 title="Example script: Matched Template Image")
284 if "subtractedExposure" in result.getDict():
285 afwDisplay.Display(frame=frame + 1).mtv(result.subtractedExposure,
286 title="Example script: Subtracted Image")
288 """
290 ConfigClass = SnapPsfMatchConfig
292 # Override ImagePsfMatchTask.subtractExposures to set doWarping on config.doWarping
293 def subtractExposures(self, templateExposure, scienceExposure,
294 templateFwhmPix=None, scienceFwhmPix=None,
295 candidateList=None):
296 return ImagePsfMatchTask.subtractExposures(self,
297 templateExposure=templateExposure,
298 scienceExposure=scienceExposure,
299 templateFwhmPix=templateFwhmPix,
300 scienceFwhmPix=scienceFwhmPix,
301 candidateList=candidateList,
302 doWarping=self.config.doWarping,
303 )